dispenso 1.4.1
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
47#pragma once
48
49#include <algorithm>
50#include <cassert>
51#include <climits>
52#include <cstring>
53#include <initializer_list>
54#include <stdexcept>
55#include <utility>
56
57#include <dispenso/detail/math.h>
58#include <dispenso/platform.h>
60
61namespace dispenso {
62
74enum class ConcurrentVectorReallocStrategy { kFullBufferAhead, kHalfBufferAhead, kAsNeeded };
75
76struct ReserveTagS {};
82
83// Textual inclusion. Includes undocumented implementation details, e.g. iterators.
84#include <dispenso/detail/concurrent_vector_impl.h>
85
90template <typename T>
96 static constexpr size_t kDefaultCapacity = (sizeof(T) >= 256) ? 2 : 512 / sizeof(T);
97
105 static constexpr size_t kMaxVectorSize =
106 (size_t{1} << (sizeof(size_t) * CHAR_BIT > 47 ? 47 : sizeof(size_t) * CHAR_BIT - 1)) /
107 sizeof(T);
108};
109
122 static constexpr bool kPreferBuffersInline = true;
123
135 ConcurrentVectorReallocStrategy::kAsNeeded;
136
146 static constexpr bool kIteratorPreferSpeed = true;
147};
148
154template <
155 typename T,
156 typename Traits = DefaultConcurrentVectorTraits,
157 typename SizeTraits = DefaultConcurrentVectorSizeTraits<T>>
159 public:
160 using value_type = T;
161 using reference = T&;
162 using const_reference = const T&;
163 using size_type = size_t;
164 using difference_type = ssize_t;
165 using reference_type = T&;
166 using const_reference_type = const T&;
167 using pointer = T*;
168 using const_pointer = const T*;
169 using iterator = std::conditional_t<
170 Traits::kIteratorPreferSpeed,
171 cv::ConcurrentVectorIterator<ConcurrentVector<T, Traits>, T, false>,
172 cv::CompactCVecIterator<ConcurrentVector<T, Traits>, T, false>>;
173 using const_iterator = std::conditional_t<
174 Traits::kIteratorPreferSpeed,
175 cv::ConcurrentVectorIterator<ConcurrentVector<T, Traits>, T, true>,
176 cv::CompactCVecIterator<ConcurrentVector<T, Traits>, T, true>>;
177 using reverse_iterator = std::reverse_iterator<iterator>;
178 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
179
183 ConcurrentVector() : ConcurrentVector(SizeTraits::kDefaultCapacity / 2, ReserveTag) {}
184
190 ConcurrentVector(size_t startCapacity, ReserveTagS)
191 : firstBucketShift_(
192 detail::log2(
193 detail::nextPow2(std::max(startCapacity, SizeTraits::kDefaultCapacity / 2)))),
194 firstBucketLen_(size_type{1} << firstBucketShift_) {
195 T* firstTwo = cv::alloc<T>(2 * firstBucketLen_);
196 buffers_[0].store(firstTwo, std::memory_order_release);
197 buffers_[1].store(firstTwo + firstBucketLen_, std::memory_order_release);
198 }
199
203 explicit ConcurrentVector(size_t startSize) : ConcurrentVector(startSize, ReserveTag) {
204 size_.store(startSize, std::memory_order_relaxed);
205 T* buf = buffers_[0].load(std::memory_order_relaxed);
206 for (size_t i = 0; i < startSize; ++i) {
207 new (buf + i) T();
208 }
209 }
210
214 ConcurrentVector(size_t startSize, const T& defaultValue)
215 : ConcurrentVector(startSize, ReserveTag) {
216 size_.store(startSize, std::memory_order_relaxed);
217 T* buf = buffers_[0].load(std::memory_order_relaxed);
218 for (size_t i = 0; i < startSize; ++i) {
219 new (buf + i) T(defaultValue);
220 }
221 }
222
226 template <typename InIterator>
227 ConcurrentVector(InIterator start, InIterator end)
228 : ConcurrentVector(std::distance(start, end), start, end) {}
229
235 template <typename InIterator>
236 ConcurrentVector(size_type startSize, InIterator start, InIterator end)
237 : ConcurrentVector(startSize, ReserveTag) {
238 size_.store(startSize, std::memory_order_relaxed);
239 assert(std::distance(start, end) == static_cast<difference_type>(startSize));
240 internalInit(start, end, begin());
241 }
242
246 ConcurrentVector(std::initializer_list<T> l)
247 : ConcurrentVector(l.size(), std::begin(l), std::end(l)) {}
248
253 : ConcurrentVector(other.size(), other.cbegin(), other.cend()) {}
254
259 : buffers_(std::move(other.buffers_)),
260 firstBucketShift_(other.firstBucketShift_),
261 firstBucketLen_(other.firstBucketLen_),
262 size_(other.size_.load(std::memory_order_relaxed)) {
263 other.size_.store(0, std::memory_order_relaxed);
264 // This is possibly unnecessary overhead, but enables the "other" vector to be in a valid,
265 // usable state right away, no empty check or clear required, as it is for std::vector.
266 T* firstTwo = cv::alloc<T>(2 * firstBucketLen_);
267 other.buffers_[0].store(firstTwo, std::memory_order_relaxed);
268 other.buffers_[1].store(firstTwo + firstBucketLen_, std::memory_order_relaxed);
269 }
270
274 ConcurrentVector& operator=(const ConcurrentVector& other) {
275 if (&other == this) {
276 return *this;
277 }
278
279 clear();
280 reserve(other.size());
281 size_.store(other.size(), std::memory_order_relaxed);
282 internalInit(other.cbegin(), other.cend(), begin());
283
284 return *this;
285 }
286
290 ConcurrentVector& operator=(ConcurrentVector&& other) {
291 using std::swap;
292 if (&other == this) {
293 return *this;
294 }
295
296 clear();
297 swap(firstBucketShift_, other.firstBucketShift_);
298 swap(firstBucketLen_, other.firstBucketLen_);
299 buffers_ = std::move(other.buffers_);
300 size_t curLen = size_.load(std::memory_order_relaxed);
301 size_.store(other.size_.load(std::memory_order_relaxed), std::memory_order_relaxed);
302 other.size_.store(curLen, std::memory_order_relaxed);
303
304 return *this;
305 }
306
313 void assign(size_type count, const T& value) {
314 clear();
315 reserve(count);
316 size_.store(count, std::memory_order_relaxed);
317 internalFillN(begin(), count, value);
318 }
319
326 template <
327 typename It,
328 typename = typename std::iterator_traits<It>::difference_type,
329 typename = typename std::iterator_traits<It>::pointer,
330 typename = typename std::iterator_traits<It>::reference,
331 typename = typename std::iterator_traits<It>::value_type,
332 typename = typename std::iterator_traits<It>::iterator_category>
333 void assign(It start, It end) {
334 clear();
335 auto count = std::distance(start, end);
336 reserve(count);
337 size_.store(count, std::memory_order_relaxed);
338 internalInit(start, end, begin());
339 }
340
346 void reserve(difference_type capacity) {
347 buffers_.allocAsNecessary({0, 0, firstBucketLen_}, capacity, bucketAndSubIndex(capacity));
348 }
349
355 void resize(difference_type len) {
356 difference_type curLen = static_cast<difference_type>(size_.load(std::memory_order_relaxed));
357 if (curLen < len) {
358 grow_to_at_least(len);
359 } else if (curLen > len) {
360 auto it = end();
361 auto newEnd = begin() + len;
362 do {
363 --it;
364 it->~T();
365 } while (it != newEnd);
366 size_.store(len, std::memory_order_relaxed);
367 }
368 }
369
376 void resize(difference_type len, const T& value) {
377 difference_type curLen = static_cast<difference_type>(size_.load(std::memory_order_relaxed));
378 if (curLen < len) {
379 grow_to_at_least(len, value);
380 } else if (curLen > len) {
381 auto it = end();
382 auto newEnd = begin() + len;
383 do {
384 --it;
385 it->~T();
386 } while (it != newEnd);
387 size_.store(len, std::memory_order_relaxed);
388 }
389 }
390
395 size_type default_capacity() const {
396 return 2 * firstBucketLen_;
397 }
398
403 size_type capacity() const {
404 size_t cap = 2 * firstBucketLen_;
405 for (size_t b = 2; b < kMaxBuffers; ++b) {
406 if (!buffers_[b].load(std::memory_order_relaxed)) {
407 break;
408 }
409 cap *= 2;
410 }
411 return cap;
412 }
413
418 void clear() {
419 auto binfo = bucketAndSubIndex(size_.load(std::memory_order_relaxed));
420 size_t len = binfo.bucketIndex;
421 size_t cap = binfo.bucketCapacity;
422 size_t b = binfo.bucket;
423 do {
424 T* buf = buffers_[b].load(std::memory_order_relaxed);
425 T* t = buf + len;
426
427 while (t != buf) {
428 --t;
429 t->~T();
430 }
431 cap >>= int{b > 1};
432 len = cap;
433 } while (b--);
434 size_.store(0, std::memory_order_release);
435 }
436
442 constexpr size_t kMaxExtra = 2;
443 auto binfo = bucketAndSubIndex(size_.load(std::memory_order_relaxed));
444
445 // We need to at least skip the first two buckets, since we have those as a single allocation.
446 size_t startBucket = std::max<size_t>(2, binfo.bucket + kMaxExtra);
447
448 for (size_t b = startBucket; b < kMaxBuffers; ++b) {
449 T* ptr = buffers_[b].load(std::memory_order_relaxed);
450 if (!ptr) {
451 break;
452 }
453 if (buffers_.shouldDealloc(b)) {
454 cv::dealloc<T>(ptr);
455 }
456 buffers_[b].store(nullptr, std::memory_order_release);
457 }
458 }
459
464 clear();
466 cv::dealloc<T>(buffers_[0].load(std::memory_order_acquire));
467 }
468
475 iterator insert(const_iterator pos, const T& value) {
476 auto it = insertPartial(pos);
477 new (&*it) T(value);
478 return it;
479 }
480
487 iterator insert(const_iterator pos, T&& value) {
488 auto it = insertPartial(pos);
489 new (&*it) T(std::move(value));
490 return it;
491 }
492
500 iterator insert(const_iterator pos, size_type count, const T& value) {
501 auto it = insertPartial(pos, count);
502 std::fill_n(it, count, value);
503 return it;
504 }
505
513 template <
514 typename InputIt,
515 typename = typename std::iterator_traits<InputIt>::difference_type,
516 typename = typename std::iterator_traits<InputIt>::pointer,
517 typename = typename std::iterator_traits<InputIt>::reference,
518 typename = typename std::iterator_traits<InputIt>::value_type,
519 typename = typename std::iterator_traits<InputIt>::iterator_category>
520 iterator insert(const_iterator pos, InputIt first, InputIt last) {
521 size_t len = std::distance(first, last);
522 auto it = insertPartial(pos, len);
523 std::copy_n(first, len, it);
524 return it;
525 }
526
533 iterator insert(const_iterator pos, std::initializer_list<T> ilist) {
534 return insert(pos, ilist.begin(), ilist.end());
535 }
536
543 iterator erase(const_iterator pos) {
544 auto e = end();
545 if (e == pos) {
546 return e;
547 }
548 size_.fetch_sub(1, std::memory_order_relaxed);
549 --e;
550 if (e == pos) {
551 e->~T();
552 return e;
553 }
554 ++e;
555 auto it = begin();
556 it += (pos - it);
557 return std::move(pos + 1, const_iterator(e), it);
558 }
559
568 iterator erase(const_iterator first, const_iterator last) {
569 size_t len = std::distance(first, last);
570 auto it = begin();
571 size_t startIdx = first - it;
572 if (len == 0) {
573 return it + (startIdx + len);
574 }
575 it += startIdx;
576
577 auto e_it = std::move(last, cend(), it);
578
579 if (e_it < last) {
580 // remove any values that were not already moved into
581 do {
582 --last;
583 last->~T();
584 } while (e_it != last);
585 }
586 size_.fetch_sub(len, std::memory_order_relaxed);
587 return e_it;
588 }
589
595 iterator push_back(const T& val) {
596 return emplace_back(val);
597 }
598
604 iterator push_back(T&& val) {
605 return emplace_back(std::move(val));
606 }
607
613 template <typename... Args>
614 iterator emplace_back(Args&&... args) {
615 auto index = size_.fetch_add(1, std::memory_order_relaxed);
616 auto binfo = bucketAndSubIndex(index);
617
618 buffers_.allocAsNecessary(binfo);
619
620 iterator ret{this, index, binfo};
621
622 if (Traits::kIteratorPreferSpeed) {
623 new (&*ret) T(std::forward<Args>(args)...);
624 } else {
625 new (buffers_[binfo.bucket] + binfo.bucketIndex) T(std::forward<Args>(args)...);
626 }
627
628 return ret;
629 }
630
638 template <typename Gen>
639 iterator grow_by_generator(size_type delta, Gen gen) {
640 iterator ret = growByUninitialized(delta);
641 for (auto it = ret; delta--; ++it) {
642 new (&*it) T(gen());
643 }
644 return ret;
645 }
646
653 iterator grow_by(size_type delta, const T& t) {
654 iterator ret = growByUninitialized(delta);
655 internalFillN(ret, delta, t);
656 return ret;
657 }
658
664 iterator grow_by(size_type delta) {
665 iterator ret = growByUninitialized(delta);
666 internalFillDefaultN(ret, delta);
667 return ret;
668 }
669
676 template <
677 typename It,
678 typename = typename std::iterator_traits<It>::difference_type,
679 typename = typename std::iterator_traits<It>::pointer,
680 typename = typename std::iterator_traits<It>::reference,
681 typename = typename std::iterator_traits<It>::value_type,
682 typename = typename std::iterator_traits<It>::iterator_category>
683 iterator grow_by(It start, It end) {
684 iterator ret = growByUninitialized(std::distance(start, end));
685 internalInit(start, end, ret);
686 return ret;
687 }
688
694 iterator grow_by(std::initializer_list<T> initList) {
695 return grow_by(std::begin(initList), std::end(initList));
696 }
697
704 iterator grow_to_at_least(size_type n) {
705 size_t curSize = size_.load(std::memory_order_relaxed);
706 if (curSize < n) {
707 return grow_by(n - curSize);
708 }
709 return {this, n - 1, bucketAndSubIndex(n - 1)};
710 }
711
719 iterator grow_to_at_least(size_type n, const T& t) {
720 size_t curSize = size_.load(std::memory_order_relaxed);
721 if (curSize < n) {
722 return grow_by(n - curSize, t);
723 }
724 return {this, n - 1, bucketAndSubIndex(n - 1)};
725 }
726
730 void pop_back() {
731 auto binfo = bucketAndSubIndex(size_.fetch_sub(1, std::memory_order_relaxed) - 1);
732 T* elt = buffers_[binfo.bucket].load(std::memory_order_relaxed) + binfo.bucketIndex;
733 elt->~T();
734 }
735
744 const T& operator[](size_type index) const {
745 auto binfo = bucketAndSubIndexForIndex(index);
746 T* buf = buffers_[binfo.bucket].load(std::memory_order_relaxed);
747 return buf[binfo.bucketIndex];
748 }
749
758 T& operator[](size_type index) {
759 auto binfo = bucketAndSubIndexForIndex(index);
760 T* buf = buffers_[binfo.bucket].load(std::memory_order_relaxed);
761 return buf[binfo.bucketIndex];
762 }
763
773 const T& at(size_type index) const {
774#if defined(__cpp_exceptions)
775 if (index >= size_.load(std::memory_order_relaxed)) {
776 throw std::out_of_range("Index too large");
777 }
778#endif
779 return operator[](index);
780 }
781
791 T& at(size_type index) {
792#if defined(__cpp_exceptions)
793 if (index >= size_.load(std::memory_order_relaxed)) {
794 throw std::out_of_range("Index too large");
795 }
796#endif
797 return operator[](index);
798 }
799
807 iterator begin() {
808 return {this, 0, {0, 0, firstBucketLen_}};
809 }
810
819 iterator end() {
820 size_t curSize = size_.load(std::memory_order_relaxed);
821 auto binfo = bucketAndSubIndex(curSize);
822 return {this, curSize, binfo};
823 }
824
832 const_iterator begin() const {
833 return {this, 0, {0, 0, firstBucketLen_}};
834 }
835
844 const_iterator end() const {
845 size_t curSize = size_.load(std::memory_order_relaxed);
846 auto binfo = bucketAndSubIndex(curSize);
847 return {this, curSize, binfo};
848 }
849
857 const_iterator cbegin() const {
858 return {this, 0, {0, 0, firstBucketLen_}};
859 }
860
869 const_iterator cend() const {
870 size_t curSize = size_.load(std::memory_order_relaxed);
871 auto binfo = bucketAndSubIndex(curSize);
872 return {this, curSize, binfo};
873 }
874
883 reverse_iterator rbegin() {
884 return reverse_iterator(end());
885 }
886
894 reverse_iterator rend() {
895 return reverse_iterator(begin());
896 }
897
906 const_reverse_iterator rbegin() const {
907 return const_reverse_iterator(cend());
908 }
909
917 const_reverse_iterator rend() const {
918 return const_reverse_iterator(cbegin());
919 }
920
926 bool empty() const {
927 return size_.load(std::memory_order_relaxed) == 0;
928 }
929
935 constexpr size_type max_size() const noexcept {
936 return Traits::kMaxVectorSize;
937 }
938
944 size_type size() const {
945 return size_.load(std::memory_order_relaxed);
946 }
947
952 T& front() {
953 return *buffers_[0].load(std::memory_order_relaxed);
954 }
955
960 const T& front() const {
961 return *buffers_[0].load(std::memory_order_relaxed);
962 }
963
969 T& back() {
970 return *(end() - 1);
971 }
972
978 const T& back() const {
979 return *(end() - 1);
980 }
981
986 void swap(ConcurrentVector& oth) {
987 using std::swap;
988 swap(firstBucketShift_, oth.firstBucketShift_);
989 swap(firstBucketLen_, oth.firstBucketLen_);
990 size_t othlen = oth.size_.load(std::memory_order_relaxed);
991 oth.size_.store(size_.load(std::memory_order_relaxed), std::memory_order_relaxed);
992 size_.store(othlen, std::memory_order_relaxed);
993
994 // okay, this relies on the fact that we're essentially swapping in the move operator.
995 buffers_ = std::move(oth.buffers_);
996 }
997
998 private:
999 DISPENSO_INLINE cv::BucketInfo bucketAndSubIndexForIndex(size_t index) const {
1000#if defined(__clang__)
1001 if (index < firstBucketLen_) {
1002 return {0, index, firstBucketLen_};
1003 }
1004
1005 size_t l2idx = detail::log2(index);
1006 size_t bucket = (l2idx + 1) - firstBucketShift_;
1007 size_t bucketCapacity = size_t{1} << l2idx;
1008 size_t bucketIndex = index - bucketCapacity;
1009
1010 return {bucket, bucketIndex, bucketCapacity};
1011#else
1012 return bucketAndSubIndex(index);
1013#endif // __clang__
1014 }
1015
1016 DISPENSO_INLINE cv::BucketInfo bucketAndSubIndex(size_t index) const {
1017 size_t l2idx = detail::log2(index | 1);
1018 size_t bucket = (l2idx + 1) - firstBucketShift_;
1019 size_t bucketCapacity = size_t{1} << l2idx;
1020 size_t bucketIndex = index - bucketCapacity;
1021
1022 bucket = index < firstBucketLen_ ? 0 : bucket;
1023 bucketIndex = index < firstBucketLen_ ? index : bucketIndex;
1024 bucketCapacity = index < firstBucketLen_ ? firstBucketLen_ : bucketCapacity;
1025
1026 return {bucket, bucketIndex, bucketCapacity};
1027 }
1028
1029 template <typename InIterator>
1030 void internalInit(InIterator start, InIterator end, iterator it) {
1031 while (start != end) {
1032 new (&*it) T(*start);
1033 ++it;
1034 ++start;
1035 }
1036 }
1037
1038 void internalFillN(iterator it, size_t len, const T& value) {
1039 for (; len--; ++it) {
1040 new (&*it) T(value);
1041 }
1042 }
1043
1044 void internalFillDefaultN(iterator it, size_t len) {
1045 for (; len--; ++it) {
1046 new (&*it) T();
1047 }
1048 }
1049
1050 iterator growByUninitialized(size_type delta) {
1051 auto index = size_.fetch_add(delta, std::memory_order_relaxed);
1052 auto binfo = bucketAndSubIndex(index);
1053 buffers_.allocAsNecessary(binfo, delta, bucketAndSubIndex(index + delta));
1054 return {this, index, binfo};
1055 }
1056
1057 iterator insertPartial(const_iterator pos) {
1058 auto e = end();
1059 auto index = size_.fetch_add(1, std::memory_order_relaxed);
1060 auto binfo = bucketAndSubIndex(index);
1061 buffers_.allocAsNecessary(binfo);
1062 new (&*e) T();
1063 return std::move_backward(pos, const_iterator(e), e + 1) - 1;
1064 }
1065
1066 iterator insertPartial(const_iterator pos, size_t len) {
1067 auto e = end();
1068 auto index = size_.fetch_add(len, std::memory_order_relaxed);
1069 buffers_.allocAsNecessary(bucketAndSubIndex(index), len, bucketAndSubIndex(index + len));
1070 for (auto it = e + len; it != e;) {
1071 --it;
1072 new (&*it) T();
1073 }
1074 return std::move_backward(pos, const_iterator(e), e + len) - len;
1075 }
1076
1077 alignas(kCacheLineSize) cv::ConVecBuffer<
1078 T,
1079 SizeTraits::kDefaultCapacity / 2,
1080 SizeTraits::kMaxVectorSize,
1081 Traits::kPreferBuffersInline,
1082 Traits::kReallocStrategy> buffers_;
1083 static constexpr size_t kMaxBuffers = decltype(buffers_)::kMaxBuffers;
1084
1085 size_t firstBucketShift_;
1086 size_t firstBucketLen_;
1087
1088 alignas(kCacheLineSize) std::atomic<size_t> size_{0};
1089
1090 friend class cv::ConVecIterBase<ConcurrentVector<T, Traits>, T>;
1091 friend class cv::ConcurrentVectorIterator<ConcurrentVector<T, Traits>, T, false>;
1092 friend class cv::ConcurrentVectorIterator<ConcurrentVector<T, Traits>, T, true>;
1093};
1094
1095template <typename T, class Traits1, class Traits2>
1096inline bool operator==(
1097 const ConcurrentVector<T, Traits1>& a,
1098 const ConcurrentVector<T, Traits2>& b) {
1099 return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin());
1100}
1101
1102template <typename T, class Traits1, class Traits2>
1103inline bool operator!=(
1104 const ConcurrentVector<T, Traits1>& a,
1105 const ConcurrentVector<T, Traits2>& b) {
1106 return !(a == b);
1107}
1108
1109template <typename T, class Traits1, class Traits2>
1110inline bool operator<(
1111 const ConcurrentVector<T, Traits1>& a,
1112 const ConcurrentVector<T, Traits2>& b) {
1113 return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end());
1114}
1115
1116template <typename T, class Traits1, class Traits2>
1117inline bool operator>(
1118 const ConcurrentVector<T, Traits1>& a,
1119 const ConcurrentVector<T, Traits2>& b) {
1120 return b < a;
1121}
1122
1123template <typename T, class Traits1, class Traits2>
1124inline bool operator<=(
1125 const ConcurrentVector<T, Traits1>& a,
1126 const ConcurrentVector<T, Traits2>& b) {
1127 return !(b < a);
1128}
1129
1130template <typename T, class Traits1, class Traits2>
1131inline bool operator>=(
1132 const ConcurrentVector<T, Traits1>& a,
1133 const ConcurrentVector<T, Traits2>& b) {
1134 return !(a < b);
1135}
1136
1137template <typename T, class Traits>
1138inline void swap(ConcurrentVector<T, Traits>& a, ConcurrentVector<T, Traits>& b) {
1139 a.swap(b);
1140}
1141
1142// Textual inclusion. Includes undocumented implementation details, e.g. iterators.
1143#include <dispenso/detail/concurrent_vector_impl2.h>
1144
1145} // namespace dispenso
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
iterator grow_to_at_least(size_type n)
ConcurrentVector(size_t startSize)
ConcurrentVector(const ConcurrentVector &other)
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)
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)
ConcurrentVectorReallocStrategy
constexpr ReserveTagS ReserveTag
constexpr size_t kCacheLineSize
A constant that defines a safe number of bytes+alignment to avoid false sharing.
Definition platform.h:89
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.
uint32_t log2(uint64_t v)
Compute log base 2 of a value (runtime).
Definition util.h:193
constexpr uint64_t nextPow2(uint64_t v)
Round up to the next power of 2.
Definition util.h:151