16#ifndef ALICEO2_ITSMFT_TRACKING_SLABBUMPALLOCATOR_H_
17#define ALICEO2_ITSMFT_TRACKING_SLABBUMPALLOCATOR_H_
25#include <memory_resource>
52 std::vector<void*>
values()
const;
56 std::unique_ptr<Impl> mImpl;
77 bool valid() const noexcept {
return n != 0; }
82 Range
grab() noexcept;
84 [[nodiscard]]
size_t capacity() const noexcept {
return mCapacity; }
85 [[nodiscard]]
size_t slab() const noexcept {
return mSlab; }
86 [[nodiscard]]
size_t watermark() const noexcept;
88 static
size_t suggestSlab(
size_t capacity,
int nThreads,
size_t minSlab = 256,
size_t maxSlab = 4096) noexcept;
93 std::atomic<
size_t> mCursor{0};
94 std::atomic<bool> mExhausted{
false};
113template <
typename T, SlabMode Mode>
116 static constexpr int32_t NoProducer = -1;
141 template <
typename... Args>
145 assert(mProducer != NoProducer);
148 if (mSlot == mSlotEnd && !refill()) {
149 mSpill.emplace_back(std::forward<Args>(args)...);
151 mSpillProducer.push_back(mProducer);
155 mSink->store(mSlot++, mProducer, std::forward<Args>(args)...);
158 [[nodiscard]]
size_t emitted() const noexcept {
return mEmitted; }
159 [[nodiscard]]
size_t spilled() const noexcept {
return mSpill.size(); }
170 const auto r = mSink->mAlloc.
grab();
177 mSlotEnd =
r.base +
r.n;
184 if (mSlot > mRunBegin) {
185 mRuns.push_back(Run{.begin = mRunBegin, .end = mSlot});
195 int32_t mProducer{NoProducer};
196 bool mDrained{
false};
198 bounded_vector<Run> mRuns;
199 bounded_vector<T> mSpill;
200 bounded_vector<int32_t> mSpillProducer;
204 :
SlabSink{cfg, grantedCapacity(cfg.capacity, cfg.nConcurrentSinks, mr), mr} {}
219 s.requested = mRequested;
221 s.memoryLimited = s.capacity < s.requested;
224 s.emitted +=
h.emitted();
225 s.spilled +=
h.spilled();
227 s.overflowed = s.spilled != 0;
235 assert(dest.get_allocator().resource()->is_equal(*mMR));
240 const auto handles = mHandles.
values();
241 for (
void*
value : handles) {
244 nRuns +=
h.mRuns.size();
247 for (
const void*
value : handles) {
249 runs.insert(runs.end(),
h.mRuns.begin(),
h.mRuns.end());
251 std::sort(runs.begin(), runs.end(), [](
const Run&
a,
const Run&
b) { return a.begin < b.begin; });
254 size_t outputSize{0};
255 for (
const auto& run : runs) {
256 for (
size_t slot{run.begin}; slot < run.end; ++slot) {
257 if (outputSize != slot) {
258 mStaging[outputSize] = std::move(mStaging[slot]);
264 mStaging.resize(outputSize);
267 for (
void*
value : handles) {
269 dest.insert(dest.end(), std::make_move_iterator(
h.mSpill.begin()), std::make_move_iterator(
h.mSpill.end()));
272 shrinkIfWasteful(dest);
283 lut.assign(nProducers + 1, 0);
285 for (
size_t s = 0; s < wm; ++s) {
286 const int32_t p = mProducerOf[s];
287 if (p != NoProducer) {
291 const auto handles = mHandles.
values();
292 for (
const void*
value : handles) {
294 for (
const int32_t p :
h.mSpillProducer) {
298 std::inclusive_scan(lut.begin(), lut.end(), lut.begin());
300 bounded_vector<int> cursor(lut.begin(), lut.begin() +
static_cast<ptrdiff_t
>(nProducers), mMR);
301 for (
size_t s = 0; s < wm; ++s) {
302 const int32_t p = mProducerOf[s];
303 mProducerOf[s] = (p != NoProducer) ? cursor[p]++ : -1;
306 const auto total =
static_cast<size_t>(lut.back());
308 for (
void*
value : handles) {
310 for (
size_t i = 0;
i <
h.mSpill.size(); ++
i) {
311 dest[cursor[
h.mSpillProducer[
i]]++] = std::move(
h.mSpill[
i]);
321 for (
size_t s = begin; s !=
end; ++s) {
322 const int d = ctx.producerOf[s];
326 (*ctx.destination)[d] = std::move(ctx.staging[s]);
337 mRequested{cfg.capacity},
338 mAlloc{granted, cfg.slabOverride ? cfg.slabOverride :
SlabBumpAllocator::suggestSlab(granted, cfg.nThreads)},
344 mStaging.resize(granted);
346 mProducerOf.assign(granted, NoProducer);
348 }
catch (
const std::bad_alloc&) {
349 discardPreallocation();
350 }
catch (
const std::length_error&) {
351 discardPreallocation();
357 const auto* bounded =
dynamic_cast<const BoundedMemoryResource*
>(mr);
358 if (bounded ==
nullptr) {
361 const size_t used = bounded->getUsedMemory();
362 const size_t limit = bounded->getMaxMemory();
363 const size_t remaining = used < limit ? limit - used : 0;
366 const size_t budget = (remaining / 2) /
static_cast<size_t>(std::max(1, nConcurrentSinks));
370 static void shrinkIfWasteful(bounded_vector<T>&
v)
372 if (
v.capacity() >
v.size() + (
v.size() / 4)) {
377 void discardPreallocation()
385 template <
typename... Args>
386 void store(
size_t slot, [[maybe_unused]] int32_t producer, Args&&... args)
388 mStaging[slot] =
T(std::forward<Args>(args)...);
390 mProducerOf[slot] = producer;
394 static void* createHandle(
void* sink)
396 return new Handle{
static_cast<SlabSink*
>(sink)};
399 static void deleteHandle(
void* handle)
401 delete static_cast<Handle*
>(handle);
405 size_t mRequested{0};
406 SlabBumpAllocator mAlloc;
407 bounded_vector<T> mStaging;
408 bounded_vector<int32_t> mProducerOf;
409 detail::ThreadLocalStorage mHandles;
410 bool mFinalized{
false};
Class for time synchronization of RawReader instances.
void resetCapacity(size_t capacity) noexcept
size_t capacity() const noexcept
size_t slab() const noexcept
size_t watermark() const noexcept
static size_t suggestSlab(size_t capacity, int nThreads, size_t minSlab=256, size_t maxSlab=4096) noexcept
size_t emitted() const noexcept
void beginProducer(int32_t p) noexcept
void emplace(Args &&... args)
size_t spilled() const noexcept
SlabSink(const Config &cfg, std::pmr::memory_resource *mr)
static constexpr size_t BytesPerSlot
void finalizeGrouped(size_t nProducers, bounded_vector< int > &lut, bounded_vector< T > &dest)
SlabSink(SlabSink &&)=delete
SlabSinkStats stats() const
std::pmr::memory_resource * memoryResource() const noexcept
void finalizeUnordered(bounded_vector< T > &dest)
SlabSink(const SlabSink &)=delete
SlabSink & operator=(const SlabSink &)=delete
SlabSink & operator=(SlabSink &&)=delete
ThreadLocalStorage(const ThreadLocalStorage &)=delete
void *(*)(void *) Factory
ThreadLocalStorage & operator=(const ThreadLocalStorage &)=delete
std::vector< void * > values() const
GLboolean GLboolean GLboolean b
GLsizei const GLfloat * value
typedef void(APIENTRYP PFNGLCULLFACEPROC)(GLenum mode)
GLboolean GLboolean GLboolean GLboolean a
void parallelFor(size_t begin, size_t end, size_t grainSize, void *context, ParallelForBody body)
void(*)(void *, size_t, size_t) ParallelForBody
void deepVectorClear(std::vector< T > &vec)
std::pmr::vector< T > bounded_vector
bool valid() const noexcept
size_t capacity
slots the memory pool actually granted
size_t requested
slots the caller predicted it would need
bool memoryLimited
the pool granted less than was requested
bool overflowed
something did not fit into the staging area
int nConcurrentSinks
sinks that may be alive on the same pool at the same time
size_t capacity
predicted number of slots
size_t slabOverride
0: derive the slab size from the granted capacity
int nThreads
workers that will feed this sink
bounded_vector< T > * destination