1+ #pragma once
2+
3+ #include " allocationBufferBase.hpp"
4+ #include " backoffScheme.hpp"
5+ #include " cacheAlligned.hpp"
6+ #include " fastRandomGenerator.hpp"
7+
8+ #include < algorithm>
9+ #include < atomic>
10+ #include < barrier>
11+ #include < cstddef>
12+ #include < random>
13+ #include < ranges>
14+ #include < vector>
15+
16+ #include < boost/container/static_vector.hpp>
17+
18+ namespace ipxp ::output {
19+
20+ template <typename ElementType>
21+ class AllocationBufferB : public AllocationBufferBase <ElementType> {
22+ constexpr static std::size_t BUCKET_SIZE = 64 ;
23+ constexpr static std::size_t INDEXES_IN_CACHE_LINE = 64 / sizeof (uint16_t );
24+ constexpr static std::size_t WINDOW_SIZE = 4 ;
25+
26+ public:
27+ explicit AllocationBufferB (const std::size_t capacity, const uint8_t writersCount) noexcept
28+ : m_objectPool(capacity + writersCount * BUCKET_SIZE)
29+ , m_buckets(m_objectPool.size() / BUCKET_SIZE)
30+ {
31+ if (capacity % BUCKET_SIZE != 0 ) {
32+ throw std::invalid_argument (" Capacity must be a multiple of bucket size" );
33+ }
34+ if (m_buckets.size () % writersCount != 0 ) {
35+ throw std::invalid_argument (" Number of buckets must be a multiple of writers count" );
36+ }
37+
38+ // m_fullBuckets.reserve(m_buckets.size());
39+ // m_emptyBuckets.reserve(m_buckets.size());
40+ for (ElementType& element : m_objectPool) {
41+ const std::size_t elementIndex = &element - m_objectPool.data ();
42+ const std::size_t bucketIndex = elementIndex / BUCKET_SIZE;
43+ m_buckets[bucketIndex].storage [elementIndex % BUCKET_SIZE] = &element;
44+ }
45+ for (std::size_t i = 0 ; i < m_buckets.size (); i++) {
46+ m_fullBuckets[i].store (i);
47+ m_emptyBuckets[i].store (Bucket::PLACEHOLDER);
48+ }
49+ m_writersData.resize (writersCount);
50+ for (uint8_t writerIndex = 0 ; writerIndex < writersCount; writerIndex++) {
51+ m_writersData[writerIndex]->fullPushRank = writerIndex * INDEXES_IN_CACHE_LINE;
52+ m_writersData[writerIndex]->emptyPushRank = writerIndex * INDEXES_IN_CACHE_LINE;
53+ m_writersData[writerIndex]->currentBucketIndex = writerIndex;
54+ m_writersData[writerIndex]->currentBucketSize = BUCKET_SIZE;
55+ m_fullBuckets[writerIndex].store (Bucket::PLACEHOLDER);
56+ }
57+ }
58+
59+ void unregisterWriter (const uint8_t writerIndex) noexcept override {}
60+
61+ ElementType* allocate (const uint8_t writerIndex) noexcept override
62+ {
63+ WriterData& writerData = m_writersData[writerIndex].get ();
64+ if (writerData.currentBucketSize == 0 ) {
65+ if (writerData.reservedEmptyBucketIndexes .size () == WINDOW_SIZE) {
66+ pushBucket (m_emptyBuckets, writerData.currentBucketIndex , writerData.emptyPushRank );
67+ } else {
68+ writerData.reservedEmptyBucketIndexes .push_back (writerData.currentBucketIndex );
69+ }
70+ if (writerData.reservedFullBucketIndexes .size () == 0 ) {
71+ while (writerData.reservedFullBucketIndexes .size () < WINDOW_SIZE) {
72+ uint16_t fullBucketIndex = popBucket (m_fullBuckets, writerData.fullPushRank );
73+ writerData.reservedFullBucketIndexes .push_back (fullBucketIndex);
74+ }
75+ }
76+ writerData.currentBucketIndex = writerData.reservedFullBucketIndexes .back ();
77+ writerData.reservedFullBucketIndexes .pop_back ();
78+ writerData.currentBucketSize = BUCKET_SIZE;
79+ }
80+ Bucket& bucket = m_buckets[writerData.currentBucketIndex ];
81+ ElementType* res = bucket.storage [writerData.currentBucketSize - 1 ];
82+ writerData.currentBucketSize --;
83+ return res;
84+ }
85+
86+ void deallocate (ElementType* element, const uint8_t writerIndex) noexcept override
87+ {
88+ WriterData& writerData = m_writersData[writerIndex].get ();
89+ if (writerData.currentBucketSize == BUCKET_SIZE) {
90+ if (writerData.reservedFullBucketIndexes .size () == WINDOW_SIZE) {
91+ pushBucket (m_fullBuckets, writerData.currentBucketIndex , writerData.fullPushRank );
92+ } else {
93+ writerData.reservedFullBucketIndexes .push_back (writerData.currentBucketIndex );
94+ }
95+ if (writerData.reservedEmptyBucketIndexes .size () == 0 ) {
96+ while (writerData.reservedEmptyBucketIndexes .size () < WINDOW_SIZE) {
97+ uint16_t emptyBucketIndex = popBucket (m_emptyBuckets, writerData.emptyPushRank );
98+ writerData.reservedEmptyBucketIndexes .push_back (emptyBucketIndex);
99+ }
100+ }
101+ writerData.currentBucketIndex = writerData.reservedEmptyBucketIndexes .back ();
102+ writerData.reservedEmptyBucketIndexes .pop_back ();
103+ writerData.currentBucketSize = 0 ;
104+ }
105+ Bucket& bucket = m_buckets[writerData.currentBucketIndex ];
106+ bucket.storage [writerData.currentBucketSize ] = element;
107+ writerData.currentBucketSize ++;
108+ }
109+
110+ private:
111+ struct WriterData {
112+ std::size_t fullPushRank;
113+ std::size_t emptyPushRank;
114+ uint16_t currentBucketIndex;
115+ std::size_t currentBucketSize {BUCKET_SIZE};
116+ std::vector<uint16_t > reservedFullBucketIndexes;
117+ std::vector<uint16_t > reservedEmptyBucketIndexes;
118+ };
119+
120+ struct Bucket {
121+ constexpr static uint16_t PLACEHOLDER = std::numeric_limits<uint16_t >::max();
122+ std::array<ElementType*, BUCKET_SIZE> storage;
123+ };
124+
125+ void pushBucket (auto & buckets, const std::size_t bucketIndex, std::size_t & pushRank) noexcept
126+ {
127+ while (true ) {
128+ uint16_t expected = buckets[pushRank].load (std::memory_order_acquire);
129+ if (expected != Bucket::PLACEHOLDER) {
130+ pushRank = ((pushRank / INDEXES_IN_CACHE_LINE + 1 ) * INDEXES_IN_CACHE_LINE)
131+ % m_buckets.size ();
132+ continue ;
133+ }
134+ if (buckets[pushRank].compare_exchange_weak (
135+ expected,
136+ bucketIndex,
137+ std::memory_order_release,
138+ std::memory_order_acquire)) {
139+ pushRank = (pushRank + 1 ) % m_buckets.size ();
140+ return ;
141+ }
142+ }
143+ }
144+
145+ uint16_t popBucket (auto & buckets, std::size_t & popRank) noexcept
146+ {
147+ while (true ) {
148+ uint16_t expected = buckets[popRank].load (std::memory_order_acquire);
149+ if (expected == Bucket::PLACEHOLDER) {
150+ popRank = ((popRank / INDEXES_IN_CACHE_LINE + 1 ) * INDEXES_IN_CACHE_LINE)
151+ % m_buckets.size ();
152+ continue ;
153+ }
154+ if (buckets[popRank].compare_exchange_weak (
155+ expected,
156+ Bucket::PLACEHOLDER,
157+ std::memory_order_release,
158+ std::memory_order_acquire)) {
159+ popRank = (popRank + 1 ) % m_buckets.size ();
160+ return expected;
161+ }
162+ }
163+ }
164+
165+ std::vector<ElementType> m_objectPool;
166+ std::vector<Bucket> m_buckets;
167+ std::array<std::atomic<uint16_t >, 65536 > m_fullBuckets;
168+ std::array<std::atomic<uint16_t >, 65536 > m_emptyBuckets;
169+ std::vector<CacheAlligned<WriterData>> m_writersData;
170+ };
171+
172+ } // namespace ipxp::output
0 commit comments