dispenso
A library for task parallelism
 
Loading...
Searching...
No Matches
concurrent_vector.h
Go to the documentation of this file.
1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
46#pragma once
47
48#include <algorithm>
49#include <cassert>
50#include <climits>
51#include <cstring>
52#include <initializer_list>
53#include <stdexcept>
54#include <utility>
55
56#include <dispenso/detail/math.h>
57#include <dispenso/platform.h>
59
60namespace dispenso {
61
73enum class ConcurrentVectorReallocStrategy { kFullBufferAhead, kHalfBufferAhead, kAsNeeded };
74
75struct ReserveTagS {};
81
82// Textual inclusion. Includes undocumented implementation details, e.g. iterators.
83#include <dispenso/detail/concurrent_vector_impl.h>
84
89template <typename T>
95 static constexpr size_t kDefaultCapacity = (sizeof(T) >= 256) ? 2 : 512 / sizeof(T);
96
104 static constexpr size_t kMaxVectorSize =
105 (size_t{1} << (sizeof(size_t) * CHAR_BIT > 47 ? 47 : sizeof(size_t) * CHAR_BIT - 1)) /
106 sizeof(T);
107};
108
121 static constexpr bool kPreferBuffersInline = true;
122
134 ConcurrentVectorReallocStrategy::kAsNeeded;
135
145 static constexpr bool kIteratorPreferSpeed = true;
146};
147
153template <
154 typename T,
158 public:
159 using value_type = T;
160 using reference = T&;
161 using const_reference = const T&;
162 using size_type = size_t;
163 using difference_type = ssize_t;
164 using reference_type = T&;
165 using const_reference_type = const T&;
166 using pointer = T*;
167 using const_pointer = const T*;
168 using iterator = std::conditional_t<
169 Traits::kIteratorPreferSpeed,
170 cv::ConcurrentVectorIterator<ConcurrentVector<T, Traits>, T, false>,
171 cv::CompactCVecIterator<ConcurrentVector<T, Traits>, T, false>>;
172 using const_iterator = std::conditional_t<
173 Traits::kIteratorPreferSpeed,
174 cv::ConcurrentVectorIterator<ConcurrentVector<T, Traits>, T, true>,
175 cv::CompactCVecIterator<ConcurrentVector<T, Traits>, T, true>>;
176 using reverse_iterator = std::reverse_iterator<iterator>;
177 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
178
183
190 : firstBucketShift_(detail::log2(
191 detail::nextPow2(std::max(startCapacity, SizeTraits::kDefaultCapacity / 2)))),
192 firstBucketLen_(size_type{1} << firstBucketShift_) {
193 T* firstTwo = cv::alloc<T>(2 * firstBucketLen_);
194 buffers_[0].store(firstTwo, std::memory_order_release);
195 buffers_[1].store(firstTwo + firstBucketLen_, std::memory_order_release);
196 }
197
202 size_.store(startSize, std::memory_order_relaxed);
203 T* buf = buffers_[0].load(std::memory_order_relaxed);
204 for (size_t i = 0; i < startSize; ++i) {
205 new (buf + i) T();
206 }
207 }
208
214 size_.store(startSize, std::memory_order_relaxed);
215 T* buf = buffers_[0].load(std::memory_order_relaxed);
216 for (size_t i = 0; i < startSize; ++i) {
217 new (buf + i) T(defaultValue);
218 }
219 }
220
224 template <typename InIterator>
227
233 template <typename InIterator>
236 size_.store(startSize, std::memory_order_relaxed);
237 assert(std::distance(start, end) == static_cast<difference_type>(startSize));
238 internalInit(start, end, begin());
239 }
240
244 ConcurrentVector(std::initializer_list<T> l)
245 : ConcurrentVector(l.size(), std::begin(l), std::end(l)) {}
246
252
257 : buffers_(std::move(other.buffers_)),
258 firstBucketShift_(other.firstBucketShift_),
259 firstBucketLen_(other.firstBucketLen_),
260 size_(other.size_.load(std::memory_order_relaxed)) {
261 other.size_.store(0, std::memory_order_relaxed);
262 // This is possibly unnecessary overhead, but enables the "other" vector to be in a valid,
263 // usable state right away, no empty check or clear required, as it is for std::vector.
264 T* firstTwo = cv::alloc<T>(2 * firstBucketLen_);
265 other.buffers_[0].store(firstTwo, std::memory_order_relaxed);
266 other.buffers_[1].store(firstTwo + firstBucketLen_, std::memory_order_relaxed);
267 }
268
273 if (&other == this) {
274 return *this;
275 }
276
277 clear();
278 reserve(other.size());
279 size_.store(other.size(), std::memory_order_relaxed);
280 internalInit(other.cbegin(), other.cend(), begin());
281
282 return *this;
283 }
284
289 using std::swap;
290 if (&other == this) {
291 return *this;
292 }
293
294 clear();
295 swap(firstBucketShift_, other.firstBucketShift_);
296 swap(firstBucketLen_, other.firstBucketLen_);
297 buffers_ = std::move(other.buffers_);
298 size_t curLen = size_.load(std::memory_order_relaxed);
299 size_.store(other.size_.load(std::memory_order_relaxed), std::memory_order_relaxed);
300 other.size_.store(curLen, std::memory_order_relaxed);
301
302 return *this;
303 }
304
311 void assign(size_type count, const T& value) {
312 clear();
313 reserve(count);
314 size_.store(count, std::memory_order_relaxed);
315 internalFillN(begin(), count, value);
316 }
317
324 template <
325 typename It,
326 typename = typename std::iterator_traits<It>::difference_type,
327 typename = typename std::iterator_traits<It>::pointer,
328 typename = typename std::iterator_traits<It>::reference,
329 typename = typename std::iterator_traits<It>::value_type,
330 typename = typename std::iterator_traits<It>::iterator_category>
331 void assign(It start, It end) {
332 clear();
333 auto count = std::distance(start, end);
334 reserve(count);
335 size_.store(count, std::memory_order_relaxed);
336 internalInit(start, end, begin());
337 }
338
344 void reserve(difference_type capacity) {
345 buffers_.allocAsNecessary({0, 0, firstBucketLen_}, capacity, bucketAndSubIndex(capacity));
346 }
347
353 void resize(difference_type len) {
354 difference_type curLen = static_cast<difference_type>(size_.load(std::memory_order_relaxed));
355 if (curLen < len) {
357 } else if (curLen > len) {
358 auto it = end();
359 auto newEnd = begin() + len;
360 do {
361 --it;
362 it->~T();
363 } while (it != newEnd);
364 size_.store(len, std::memory_order_relaxed);
365 }
366 }
367
374 void resize(difference_type len, const T& value) {
375 difference_type curLen = static_cast<difference_type>(size_.load(std::memory_order_relaxed));
376 if (curLen < len) {
378 } else if (curLen > len) {
379 auto it = end();
380 auto newEnd = begin() + len;
381 do {
382 --it;
383 it->~T();
384 } while (it != newEnd);
385 size_.store(len, std::memory_order_relaxed);
386 }
387 }
388
393 size_type default_capacity() const {
394 return 2 * firstBucketLen_;
395 }
396
401 size_type capacity() const {
402 size_t cap = 2 * firstBucketLen_;
403 for (size_t b = 2; b < kMaxBuffers; ++b) {
404 if (!buffers_[b].load(std::memory_order_relaxed)) {
405 break;
406 }
407 cap *= 2;
408 }
409 return cap;
410 }
411
416 void clear() {
417 auto binfo = bucketAndSubIndex(size_.load(std::memory_order_relaxed));
418 size_t len = binfo.bucketIndex;
419 size_t cap = binfo.bucketCapacity;
420 size_t b = binfo.bucket;
421 do {
422 T* buf = buffers_[b].load(std::memory_order_relaxed);
423 T* t = buf + len;
424
425 while (t != buf) {
426 --t;
427 t->~T();
428 }
429 cap >>= int{b > 1};
430 len = cap;
431 } while (b--);
432 size_.store(0, std::memory_order_release);
433 }
434
440 constexpr size_t kMaxExtra = 2;
441 auto binfo = bucketAndSubIndex(size_.load(std::memory_order_relaxed));
442
443 // We need to at least skip the first two buckets, since we have those as a single allocation.
444 size_t startBucket = std::max<size_t>(2, binfo.bucket + kMaxExtra);
445
446 for (size_t b = startBucket; b < kMaxBuffers; ++b) {
447 T* ptr = buffers_[b].load(std::memory_order_relaxed);
448 if (!ptr) {
449 break;
450 }
451 if (buffers_.shouldDealloc(b)) {
452 cv::dealloc<T>(ptr);
453 }
454 buffers_[b].store(nullptr, std::memory_order_release);
455 }
456 }
457
462 clear();
464 cv::dealloc<T>(buffers_[0].load(std::memory_order_acquire));
465 }
466
473 iterator insert(const_iterator pos, const T& value) {
474 auto it = insertPartial(pos);
475 new (&*it) T(value);
476 return it;
477 }
478
485 iterator insert(const_iterator pos, T&& value) {
486 auto it = insertPartial(pos);
487 new (&*it) T(std::move(value));
488 return it;
489 }
490
498 iterator insert(const_iterator pos, size_type count, const T& value) {
499 auto it = insertPartial(pos, count);
500 std::fill_n(it, count, value);
501 return it;
502 }
503
511 template <
512 typename InputIt,
513 typename = typename std::iterator_traits<InputIt>::difference_type,
514 typename = typename std::iterator_traits<InputIt>::pointer,
515 typename = typename std::iterator_traits<InputIt>::reference,
516 typename = typename std::iterator_traits<InputIt>::value_type,
517 typename = typename std::iterator_traits<InputIt>::iterator_category>
518 iterator insert(const_iterator pos, InputIt first, InputIt last) {
519 size_t len = std::distance(first, last);
520 auto it = insertPartial(pos, len);
521 std::copy_n(first, len, it);
522 return it;
523 }
524
531 iterator insert(const_iterator pos, std::initializer_list<T> ilist) {
532 return insert(pos, ilist.begin(), ilist.end());
533 }
534
541 iterator erase(const_iterator pos) {
542 auto e = end();
543 if (e == pos) {
544 return e;
545 }
546 size_.fetch_sub(1, std::memory_order_relaxed);
547 --e;
548 if (e == pos) {
549 e->~T();
550 return e;
551 }
552 ++e;
553 auto it = begin();
554 it += (pos - it);
555 return std::move(pos + 1, const_iterator(e), it);
556 }
557
566 iterator erase(const_iterator first, const_iterator last) {
567 size_t len = std::distance(first, last);
568 auto it = begin();
569 size_t startIdx = first - it;
570 if (len == 0) {
571 return it + (startIdx + len);
572 }
573 it += startIdx;
574
575 auto e_it = std::move(last, cend(), it);
576
577 if (e_it < last) {
578 // remove any values that were not already moved into
579 do {
580 --last;
581 last->~T();
582 } while (e_it != last);
583 }
584 size_.fetch_sub(len, std::memory_order_relaxed);
585 return e_it;
586 }
587
593 iterator push_back(const T& val) {
594 return emplace_back(val);
595 }
596
602 iterator push_back(T&& val) {
603 return emplace_back(std::move(val));
604 }
605
611 template <typename... Args>
612 iterator emplace_back(Args&&... args) {
613 auto index = size_.fetch_add(1, std::memory_order_relaxed);
614 auto binfo = bucketAndSubIndex(index);
615
616 buffers_.allocAsNecessary(binfo);
617
618 iterator ret{this, index, binfo};
619
620 if (Traits::kIteratorPreferSpeed) {
621 new (&*ret) T(std::forward<Args>(args)...);
622 } else {
623 new (buffers_[binfo.bucket] + binfo.bucketIndex) T(std::forward<Args>(args)...);
624 }
625
626 return ret;
627 }
628
636 template <typename Gen>
637 iterator grow_by_generator(size_type delta, Gen gen) {
638 iterator ret = growByUninitialized(delta);
639 for (auto it = ret; delta--; ++it) {
640 new (&*it) T(gen());
641 }
642 return ret;
643 }
644
651 iterator grow_by(size_type delta, const T& t) {
652 iterator ret = growByUninitialized(delta);
653 internalFillN(ret, delta, t);
654 return ret;
655 }
656
662 iterator grow_by(size_type delta) {
663 iterator ret = growByUninitialized(delta);
664 internalFillDefaultN(ret, delta);
665 return ret;
666 }
667
674 template <
675 typename It,
676 typename = typename std::iterator_traits<It>::difference_type,
677 typename = typename std::iterator_traits<It>::pointer,
678 typename = typename std::iterator_traits<It>::reference,
679 typename = typename std::iterator_traits<It>::value_type,
680 typename = typename std::iterator_traits<It>::iterator_category>
681 iterator grow_by(It start, It end) {
682 iterator ret = growByUninitialized(std::distance(start, end));
683 internalInit(start, end, ret);
684 return ret;
685 }
686
692 iterator grow_by(std::initializer_list<T> initList) {
693 return grow_by(std::begin(initList), std::end(initList));
694 }
695
702 iterator grow_to_at_least(size_type n) {
703 size_t curSize = size_.load(std::memory_order_relaxed);
704 if (curSize < n) {
705 return grow_by(n - curSize);
706 }
707 return {this, n - 1, bucketAndSubIndex(n - 1)};
708 }
709
717 iterator grow_to_at_least(size_type n, const T& t) {
718 size_t curSize = size_.load(std::memory_order_relaxed);
719 if (curSize < n) {
720 return grow_by(n - curSize, t);
721 }
722 return {this, n - 1, bucketAndSubIndex(n - 1)};
723 }
724
728 void pop_back() {
729 auto binfo = bucketAndSubIndex(size_.fetch_sub(1, std::memory_order_relaxed) - 1);
730 T* elt = buffers_[binfo.bucket].load(std::memory_order_relaxed) + binfo.bucketIndex;
731 elt->~T();
732 }
733
742 const T& operator[](size_type index) const {
743 auto binfo = bucketAndSubIndexForIndex(index);
744 T* buf = buffers_[binfo.bucket].load(std::memory_order_relaxed);
745 return buf[binfo.bucketIndex];
746 }
747
756 T& operator[](size_type index) {
757 auto binfo = bucketAndSubIndexForIndex(index);
758 T* buf = buffers_[binfo.bucket].load(std::memory_order_relaxed);
759 return buf[binfo.bucketIndex];
760 }
761
771 const T& at(size_type index) const {
772#if defined(__cpp_exceptions)
773 if (index >= size_.load(std::memory_order_relaxed)) {
774 throw std::out_of_range("Index too large");
775 }
776#endif
777 return operator[](index);
778 }
779
789 T& at(size_type index) {
790#if defined(__cpp_exceptions)
791 if (index >= size_.load(std::memory_order_relaxed)) {
792 throw std::out_of_range("Index too large");
793 }
794#endif
795 return operator[](index);
796 }
797
805 iterator begin() {
806 return {this, 0, {0, 0, firstBucketLen_}};
807 }
808
817 iterator end() {
818 size_t curSize = size_.load(std::memory_order_relaxed);
819 auto binfo = bucketAndSubIndex(curSize);
820 return {this, curSize, binfo};
821 }
822
830 const_iterator begin() const {
831 return {this, 0, {0, 0, firstBucketLen_}};
832 }
833
842 const_iterator end() const {
843 size_t curSize = size_.load(std::memory_order_relaxed);
844 auto binfo = bucketAndSubIndex(curSize);
845 return {this, curSize, binfo};
846 }
847
855 const_iterator cbegin() const {
856 return {this, 0, {0, 0, firstBucketLen_}};
857 }
858
867 const_iterator cend() const {
868 size_t curSize = size_.load(std::memory_order_relaxed);
869 auto binfo = bucketAndSubIndex(curSize);
870 return {this, curSize, binfo};
871 }
872
881 reverse_iterator rbegin() {
882 return reverse_iterator(end());
883 }
884
892 reverse_iterator rend() {
893 return reverse_iterator(begin());
894 }
895
904 const_reverse_iterator rbegin() const {
905 return const_reverse_iterator(cend());
906 }
907
915 const_reverse_iterator rend() const {
916 return const_reverse_iterator(cbegin());
917 }
918
924 bool empty() const {
925 return size_.load(std::memory_order_relaxed) == 0;
926 }
927
933 constexpr size_type max_size() const noexcept {
934 return Traits::kMaxVectorSize;
935 }
936
942 size_type size() const {
943 return size_.load(std::memory_order_relaxed);
944 }
945
950 T& front() {
951 return *buffers_[0].load(std::memory_order_relaxed);
952 }
953
958 const T& front() const {
959 return *buffers_[0].load(std::memory_order_relaxed);
960 }
961
967 T& back() {
968 return *(end() - 1);
969 }
970
976 const T& back() const {
977 return *(end() - 1);
978 }
979
985 using std::swap;
986 swap(firstBucketShift_, oth.firstBucketShift_);
987 swap(firstBucketLen_, oth.firstBucketLen_);
988 size_t othlen = oth.size_.load(std::memory_order_relaxed);
989 oth.size_.store(size_.load(std::memory_order_relaxed), std::memory_order_relaxed);
990 size_.store(othlen, std::memory_order_relaxed);
991
992 // okay, this relies on the fact that we're essentially swapping in the move operator.
993 buffers_ = std::move(oth.buffers_);
994 }
995
996 private:
997 DISPENSO_INLINE cv::BucketInfo bucketAndSubIndexForIndex(size_t index) const {
998#if defined(__clang__)
999 if (index < firstBucketLen_) {
1000 return {0, index, firstBucketLen_};
1001 }
1002
1003 size_t l2idx = detail::log2(index);
1004 size_t bucket = (l2idx + 1) - firstBucketShift_;
1005 size_t bucketCapacity = size_t{1} << l2idx;
1006 size_t bucketIndex = index - bucketCapacity;
1007
1008 return {bucket, bucketIndex, bucketCapacity};
1009#else
1010 return bucketAndSubIndex(index);
1011#endif // __clang__
1012 }
1013
1014 DISPENSO_INLINE cv::BucketInfo bucketAndSubIndex(size_t index) const {
1015 size_t l2idx = detail::log2(index | 1);
1016 size_t bucket = (l2idx + 1) - firstBucketShift_;
1017 size_t bucketCapacity = size_t{1} << l2idx;
1018 size_t bucketIndex = index - bucketCapacity;
1019
1020 bucket = index < firstBucketLen_ ? 0 : bucket;
1021 bucketIndex = index < firstBucketLen_ ? index : bucketIndex;
1022 bucketCapacity = index < firstBucketLen_ ? firstBucketLen_ : bucketCapacity;
1023
1024 return {bucket, bucketIndex, bucketCapacity};
1025 }
1026
1027 template <typename InIterator>
1028 void internalInit(InIterator start, InIterator end, iterator it) {
1029 while (start != end) {
1030 new (&*it) T(*start);
1031 ++it;
1032 ++start;
1033 }
1034 }
1035
1036 void internalFillN(iterator it, size_t len, const T& value) {
1037 for (; len--; ++it) {
1038 new (&*it) T(value);
1039 }
1040 }
1041
1042 void internalFillDefaultN(iterator it, size_t len) {
1043 for (; len--; ++it) {
1044 new (&*it) T();
1045 }
1046 }
1047
1048 iterator growByUninitialized(size_type delta) {
1049 auto index = size_.fetch_add(delta, std::memory_order_relaxed);
1050 auto binfo = bucketAndSubIndex(index);
1051 buffers_.allocAsNecessary(binfo, delta, bucketAndSubIndex(index + delta));
1052 return {this, index, binfo};
1053 }
1054
1055 iterator insertPartial(const_iterator pos) {
1056 auto e = end();
1057 auto index = size_.fetch_add(1, std::memory_order_relaxed);
1058 auto binfo = bucketAndSubIndex(index);
1059 buffers_.allocAsNecessary(binfo);
1060 new (&*e) T();
1061 return std::move_backward(pos, const_iterator(e), e + 1) - 1;
1062 }
1063
1064 iterator insertPartial(const_iterator pos, size_t len) {
1065 auto e = end();
1066 auto index = size_.fetch_add(len, std::memory_order_relaxed);
1067 buffers_.allocAsNecessary(bucketAndSubIndex(index), len, bucketAndSubIndex(index + len));
1068 for (auto it = e + len; it != e;) {
1069 --it;
1070 new (&*it) T();
1071 }
1072 return std::move_backward(pos, const_iterator(e), e + len) - len;
1073 }
1074
1075 alignas(kCacheLineSize) cv::ConVecBuffer<
1076 T,
1077 SizeTraits::kDefaultCapacity / 2,
1078 SizeTraits::kMaxVectorSize,
1079 Traits::kPreferBuffersInline,
1080 Traits::kReallocStrategy> buffers_;
1081 static constexpr size_t kMaxBuffers = decltype(buffers_)::kMaxBuffers;
1082
1083 size_t firstBucketShift_;
1084 size_t firstBucketLen_;
1085
1086 alignas(kCacheLineSize) std::atomic<size_t> size_{0};
1087
1088 friend class cv::ConVecIterBase<ConcurrentVector<T, Traits>, T>;
1089 friend class cv::ConcurrentVectorIterator<ConcurrentVector<T, Traits>, T, false>;
1090 friend class cv::ConcurrentVectorIterator<ConcurrentVector<T, Traits>, T, true>;
1091};
1092
1093template <typename T, class Traits1, class Traits2>
1094inline bool operator==(
1095 const ConcurrentVector<T, Traits1>& a,
1096 const ConcurrentVector<T, Traits2>& b) {
1097 return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin());
1098}
1099
1100template <typename T, class Traits1, class Traits2>
1101inline bool operator!=(
1102 const ConcurrentVector<T, Traits1>& a,
1103 const ConcurrentVector<T, Traits2>& b) {
1104 return !(a == b);
1105}
1106
1107template <typename T, class Traits1, class Traits2>
1108inline bool operator<(
1109 const ConcurrentVector<T, Traits1>& a,
1110 const ConcurrentVector<T, Traits2>& b) {
1111 return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end());
1112}
1113
1114template <typename T, class Traits1, class Traits2>
1115inline bool operator>(
1116 const ConcurrentVector<T, Traits1>& a,
1117 const ConcurrentVector<T, Traits2>& b) {
1118 return b < a;
1119}
1120
1121template <typename T, class Traits1, class Traits2>
1122inline bool operator<=(
1123 const ConcurrentVector<T, Traits1>& a,
1124 const ConcurrentVector<T, Traits2>& b) {
1125 return !(b < a);
1126}
1127
1128template <typename T, class Traits1, class Traits2>
1129inline bool operator>=(
1130 const ConcurrentVector<T, Traits1>& a,
1131 const ConcurrentVector<T, Traits2>& b) {
1132 return !(a < b);
1133}
1134
1135template <typename T, class Traits>
1136inline void swap(ConcurrentVector<T, Traits>& a, ConcurrentVector<T, Traits>& b) {
1137 a.swap(b);
1138}
1139
1140// Textual inclusion. Includes undocumented implementation details, e.g. iterators.
1141#include <dispenso/detail/concurrent_vector_impl2.h>
1142
1143} // namespace dispenso
ConcurrentVector & operator=(const ConcurrentVector &other)
iterator insert(const_iterator pos, const T &value)
iterator grow_by(It start, It end)
const_reverse_iterator rend() const
iterator emplace_back(Args &&... args)
iterator push_back(const T &val)
iterator erase(const_iterator first, const_iterator last)
const_iterator end() const
ConcurrentVector(size_t startSize, const T &defaultValue)
void assign(size_type count, const T &value)
void resize(difference_type len)
void reserve(difference_type capacity)
iterator insert(const_iterator pos, T &&value)
iterator insert(const_iterator pos, size_type count, const T &value)
const_reverse_iterator rbegin() const
const T & operator[](size_type index) const
iterator grow_to_at_least(size_type n)
ConcurrentVector(size_t startSize)
ConcurrentVector(const ConcurrentVector &other)
T & operator[](size_type index)
iterator grow_to_at_least(size_type n, const T &t)
const_iterator cend() const
constexpr size_type max_size() const noexcept
void resize(difference_type len, const T &value)
const_iterator begin() const
void assign(It start, It end)
const_iterator cbegin() const
size_type default_capacity() const
iterator insert(const_iterator pos, InputIt first, InputIt last)
iterator grow_by(std::initializer_list< T > initList)
ConcurrentVector(size_type startSize, InIterator start, InIterator end)
ConcurrentVector(std::initializer_list< T > l)
iterator insert(const_iterator pos, std::initializer_list< T > ilist)
iterator grow_by(size_type delta)
void swap(ConcurrentVector &oth)
iterator grow_by_generator(size_type delta, Gen gen)
ConcurrentVector(InIterator start, InIterator end)
ConcurrentVector(size_t startCapacity, ReserveTagS)
iterator erase(const_iterator pos)
const T & at(size_type index) const
ConcurrentVector(ConcurrentVector &&other)
iterator grow_by(size_type delta, const T &t)
ConcurrentVector & operator=(ConcurrentVector &&other)
ConcurrentVectorReallocStrategy
constexpr ReserveTagS ReserveTag
detail::OpResult< T > OpResult
Definition pipeline.h:29
static constexpr size_t kDefaultCapacity
This is the starting user-expected capacity in number of elements (algorithm may provide more based o...
static constexpr size_t kMaxVectorSize
The maximum possible size for the vector.
static constexpr bool kPreferBuffersInline
Prefer to place the pointers to the buffers inline in the class.
static constexpr ConcurrentVectorReallocStrategy kReallocStrategy
How far/whether to allocate before memory is required.
static constexpr bool kIteratorPreferSpeed
Should we prefer faster, but larger iterators, or slower, but smaller iterators.