Project
Loading...
Searching...
No Matches
testSlabBumpAllocator.cxx
Go to the documentation of this file.
1// Copyright 2019-2026 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
12#define BOOST_TEST_MODULE Test SlabBumpAllocator
13#define BOOST_TEST_MAIN
14#define BOOST_TEST_DYN_LINK
15
16#include <boost/test/unit_test.hpp>
17
18#include <algorithm>
19#include <cstdint>
20#include <limits>
21#include <memory_resource>
22#include <new>
23#include <random>
24#include <vector>
25
26#include <oneapi/tbb/task_arena.h>
27
31
32using namespace o2::its;
33
34namespace
35{
36
37struct Rec {
38 int a{-1};
39 int b{-1};
40 float payload{0.f};
41 Rec() = default;
42 Rec(int aa, int bb, float p) : a{aa}, b{bb}, payload{p} {}
43 bool operator<(const Rec& o) const
44 {
45 if ((a < 0) != (o.a < 0)) {
46 return o.a < 0;
47 }
48 return a != o.a ? a < o.a : b < o.b;
49 }
50 bool operator==(const Rec& o) const { return a == o.a && b == o.b; }
51};
52
53std::ostream& operator<<(std::ostream& os, const Rec& r)
54{
55 return os << "Rec{" << r.a << ',' << r.b << ',' << r.payload << '}';
56}
57
58class StingyResource final : public std::pmr::memory_resource
59{
60 public:
61 explicit StingyResource(size_t maxBytes) : mMax{maxBytes} {}
62
63 private:
64 void* do_allocate(size_t bytes, size_t alignment) final
65 {
66 if (bytes > mMax) {
67 throw std::bad_alloc{};
68 }
69 return std::pmr::new_delete_resource()->allocate(bytes, alignment);
70 }
71 void do_deallocate(void* p, size_t bytes, size_t alignment) final
72 {
73 std::pmr::new_delete_resource()->deallocate(p, bytes, alignment);
74 }
75 bool do_is_equal(const std::pmr::memory_resource& other) const noexcept final { return this == &other; }
76
77 size_t mMax;
78};
79
80template <typename F>
81void runConcurrently(F&& f)
82{
83 tbb::task_arena arena{4};
84 arena.execute(std::forward<F>(f));
85}
86
87template <typename Emit>
88void produce(int i, uint32_t seed, Emit&& emit)
89{
90 std::mt19937 rng(seed + (uint32_t(i) * 2654435761u));
91 const int n = int(rng() % 12);
92 for (int k = 0; k < n; ++k) {
93 emit(i, k, float((i * 100) + k));
94 }
95}
96
97std::vector<std::vector<Rec>> reference(int nProducers, uint32_t seed)
98{
99 std::vector<std::vector<Rec>> out(nProducers);
100 for (int i = 0; i < nProducers; ++i) {
101 produce(i, seed, [&](int a, int b, float p) { out[i].emplace_back(a, b, p); });
102 }
103 return out;
104}
105
106void checkGrouped(int nProducers, size_t capacity, size_t slab, size_t maxMemory = std::numeric_limits<size_t>::max())
107{
108 constexpr uint32_t seed = 7u;
109 BoundedMemoryResource mr{maxMemory};
110
111 const auto ref = reference(nProducers, seed);
112 std::vector<Rec> flat;
113 std::vector<int> refLut(nProducers + 1, 0);
114 for (int i = 0; i < nProducers; ++i) {
115 refLut[i + 1] = refLut[i] + int(ref[i].size());
116 flat.insert(flat.end(), ref[i].begin(), ref[i].end());
117 }
118
119 GroupedSlabSink<Rec> sink{{.capacity = capacity, .nThreads = 4, .slabOverride = slab}, &mr};
120 runConcurrently([&] {
121 tbb::parallel_for(0, nProducers, [&](int i) {
122 auto& h = sink.local();
123 h.beginProducer(i);
124 produce(i, seed, [&](int a, int b, float p) { h.emplace(a, b, p); });
125 });
126 });
127
128 const auto st = sink.stats();
129 BOOST_TEST(st.emitted == flat.size());
130
131 bounded_vector<int> lut{&mr};
132 bounded_vector<Rec> dest{&mr};
133 sink.finalizeGrouped(size_t(nProducers), lut, dest);
134
135 BOOST_REQUIRE(lut.size() == size_t(nProducers) + 1);
136 BOOST_TEST(std::equal(lut.begin(), lut.end(), refLut.begin()));
137 BOOST_REQUIRE(dest.size() == flat.size());
138 for (size_t i = 0; i < flat.size(); ++i) {
139 BOOST_TEST(dest[i] == flat[i]);
140 BOOST_TEST(dest[i].payload == flat[i].payload);
141 }
142}
143
144void checkUnordered(int nProducers, size_t capacity, size_t slab, size_t maxMemory = std::numeric_limits<size_t>::max())
145{
146 constexpr uint32_t seed = 11u;
147 BoundedMemoryResource mr{maxMemory};
148
149 const auto ref = reference(nProducers, seed);
150 std::vector<Rec> flat;
151 for (const auto& v : ref) {
152 flat.insert(flat.end(), v.begin(), v.end());
153 }
154 std::sort(flat.begin(), flat.end());
155 flat.erase(std::unique(flat.begin(), flat.end()), flat.end());
156
157 UnorderedSlabSink<Rec> sink{{.capacity = capacity, .nThreads = 4, .slabOverride = slab}, &mr};
158 runConcurrently([&] {
159 tbb::parallel_for(0, nProducers, [&](int i) {
160 auto& h = sink.local();
161 produce(i, seed, [&](int a, int b, float p) { h.emplace(a, b, p); });
162 });
163 });
164
165 const auto st = sink.stats();
166 BOOST_TEST(st.emitted == flat.size());
167
168 bounded_vector<Rec> dest{&mr};
169 sink.finalizeUnordered(dest);
170
171 std::sort(dest.begin(), dest.end());
172
173 BOOST_REQUIRE(dest.size() == flat.size());
174 for (size_t i = 0; i < flat.size(); ++i) {
175 BOOST_TEST(dest[i] == flat[i]);
176 BOOST_TEST(dest[i].payload == flat[i].payload);
177 }
178}
179
180} // namespace
181
182BOOST_AUTO_TEST_CASE(slab_hands_out_disjoint_ranges)
183{
184 SlabBumpAllocator alloc{1000, 256};
185 std::vector<char> seen(1000, 0);
186 size_t got{0};
187 while (true) {
188 const auto r = alloc.grab();
189 if (!r.valid()) {
190 break;
191 }
192 BOOST_REQUIRE(r.base + r.n <= 1000);
193 for (size_t s = r.base; s < r.base + r.n; ++s) {
194 BOOST_REQUIRE(seen[s] == 0);
195 seen[s] = 1;
196 }
197 got += r.n;
198 }
199 BOOST_TEST(got == 1000u);
200 BOOST_TEST(alloc.watermark() <= 1000u);
201}
202
203BOOST_AUTO_TEST_CASE(slab_never_exceeds_a_threads_fair_share)
204{
207 BOOST_TEST(SlabBumpAllocator::suggestSlab(1u << 20, 8) == 4096u);
208}
209
210BOOST_AUTO_TEST_CASE(grouped_reproduces_two_pass_layout)
211{
212 checkGrouped(2000, 40000, 512);
213 checkGrouped(300, 20000, 4096);
214}
215
216BOOST_AUTO_TEST_CASE(grouped_survives_capacity_underestimate)
217{
218 checkGrouped(2000, 3000, 256);
219 checkGrouped(500, 0, 1, 1u << 20);
220}
221
222BOOST_AUTO_TEST_CASE(grouped_survives_capacity_overestimate)
223{
224 checkGrouped(20, 1u << 20, 256, 1u << 16);
225}
226
227BOOST_AUTO_TEST_CASE(grouped_keeps_order_across_slab_and_spill_boundaries)
228{
230 const std::vector<int> counts{3, 5, 6, 0, 2};
231 GroupedSlabSink<Rec> sink{{.capacity = 10, .nThreads = 1, .slabOverride = 4}, &mr};
232
233 auto& h = sink.local();
234 for (size_t p = 0; p < counts.size(); ++p) {
235 h.beginProducer(int(p));
236 for (int k = 0; k < counts[p]; ++k) {
237 h.emplace(int(p), k, float(k));
238 }
239 }
240 const auto st = sink.stats();
241 BOOST_TEST(st.emitted == 16u);
242 BOOST_TEST(st.spilled == 6u); // capacity 10 of 16
243 BOOST_TEST(st.overflowed);
244
245 bounded_vector<int> lut{&mr};
246 bounded_vector<Rec> dest{&mr};
247 sink.finalizeGrouped(counts.size(), lut, dest);
248
249 BOOST_REQUIRE(lut.size() == counts.size() + 1);
250 BOOST_REQUIRE(dest.size() == 16u);
251 int expected{0};
252 for (size_t p = 0; p < counts.size(); ++p) {
253 BOOST_TEST(lut[p] == expected);
254 for (int k = 0; k < counts[p]; ++k) {
255 BOOST_TEST(dest[expected + k] == Rec(int(p), k, 0.f));
256 }
257 expected += counts[p];
258 }
259 BOOST_TEST(lut.back() == expected);
260}
261
262BOOST_AUTO_TEST_CASE(unordered_reproduces_emitted_records)
263{
264 checkUnordered(2000, 40000, 512);
265 checkUnordered(300, 20000, 4096);
266}
267
268BOOST_AUTO_TEST_CASE(unordered_survives_capacity_underestimate)
269{
270 checkUnordered(2000, 3000, 256);
271 checkUnordered(500, 0, 1, 1u << 20);
272}
273
274BOOST_AUTO_TEST_CASE(unordered_keeps_records_across_slab_and_spill_boundaries)
275{
277 UnorderedSlabSink<Rec> sink{{.capacity = 10, .nThreads = 1, .slabOverride = 4}, &mr};
278
279 auto& h = sink.local();
280 for (int i = 0; i < 14; ++i) {
281 h.emplace(i, i + 1, float(i));
282 }
283 const auto st = sink.stats();
284 BOOST_TEST(st.emitted == 14u);
285 BOOST_TEST(st.spilled == 4u);
286
287 bounded_vector<Rec> dest{&mr};
288 sink.finalizeUnordered(dest);
289
290 BOOST_REQUIRE(dest.size() == 14u);
291 for (int i = 0; i < 14; ++i) {
292 BOOST_TEST(dest[i] == Rec(i, i + 1, float(i)));
293 }
294}
295
296BOOST_AUTO_TEST_CASE(unordered_removes_unused_slots)
297{
299 UnorderedSlabSink<Rec> sink{{.capacity = 10, .nThreads = 1, .slabOverride = 4}, &mr};
300 sink.local().emplace(1, 2, 3.f);
301 sink.local().emplace();
302
303 bounded_vector<Rec> dest{&mr};
304 sink.finalizeUnordered(dest);
305
306 BOOST_REQUIRE(dest.size() == 2u);
307 BOOST_TEST(dest.front() == Rec(1, 2, 3.f));
308 BOOST_TEST(dest.front().payload == 3.f);
309 BOOST_TEST(dest.back() == Rec{});
310}
311
312BOOST_AUTO_TEST_CASE(unordered_does_not_hand_back_an_oversized_buffer)
313{
315 UnorderedSlabSink<Rec> sink{{.capacity = 100000, .nThreads = 1, .slabOverride = 256}, &mr};
316
317 auto& h = sink.local();
318 for (int i = 0; i < 100; ++i) {
319 h.emplace(i, i + 1, float(i));
320 }
321 bounded_vector<Rec> dest{&mr};
322 sink.finalizeUnordered(dest);
323
324 BOOST_REQUIRE(dest.size() == 100u);
325 BOOST_TEST(dest.capacity() < 1000u);
326}
327
328BOOST_AUTO_TEST_CASE(capacity_is_clamped_to_what_the_pool_can_spare)
329{
330 constexpr size_t maxMemory = 1u << 16;
331 BoundedMemoryResource mr{maxMemory};
332 UnorderedSlabSink<Rec> sink{{.capacity = 1u << 20, .nThreads = 4}, &mr};
333
334 const auto st = sink.stats();
335 BOOST_TEST(st.requested == size_t{1u << 20});
336 BOOST_TEST(st.capacity > 0u);
337 BOOST_TEST(st.capacity < st.requested);
338 BOOST_TEST(st.memoryLimited);
339 BOOST_TEST(st.capacity * sizeof(Rec) <= maxMemory / 2);
340}
341
342BOOST_AUTO_TEST_CASE(capacity_is_split_between_concurrent_sinks)
343{
344 size_t alone{0}, shared{0};
345 {
346 BoundedMemoryResource mr{1u << 16};
347 UnorderedSlabSink<Rec> sink{{.capacity = 1u << 20, .nThreads = 4, .nConcurrentSinks = 1}, &mr};
348 alone = sink.stats().capacity;
349 }
350 {
351 BoundedMemoryResource mr{1u << 16};
352 UnorderedSlabSink<Rec> sink{{.capacity = 1u << 20, .nThreads = 4, .nConcurrentSinks = 4}, &mr};
353 shared = sink.stats().capacity;
354 }
355 BOOST_TEST(shared > 0u);
356 BOOST_TEST(shared < alone);
357 BOOST_TEST(shared * 4 <= alone + 8); // integer division slack
358}
359
360BOOST_AUTO_TEST_CASE(unordered_survives_a_failed_preallocation)
361{
362 StingyResource mr{1u << 12};
363 UnorderedSlabSink<Rec> sink{{.capacity = 1u << 20, .nThreads = 1}, &mr};
364
365 const auto st = sink.stats();
366 BOOST_TEST(st.capacity == 0u);
367 BOOST_TEST(st.memoryLimited);
368
369 auto& handle = sink.local();
370 for (int i = 0; i < 10; ++i) {
371 handle.emplace(i, i + 1, float(i));
372 }
373
374 bounded_vector<Rec> dest{&mr};
375 sink.finalizeUnordered(dest);
376 BOOST_REQUIRE(dest.size() == 10u);
377 for (int i = 0; i < 10; ++i) {
378 BOOST_TEST(dest[i] == Rec(i, i + 1, float(i)));
379 }
380}
381
382BOOST_AUTO_TEST_CASE(estimator_cold_start_has_capacity)
383{
385 const auto key = CapacityEstimator::makeKey(SlabSite::Cells, 0, 0, 3);
386 BOOST_TEST(est.capacity(key, 1000.) == 1024u);
387
388 est.update(key, 0., 0, 0, false, false);
389 BOOST_TEST(est.capacity(key, 0.) == 0u);
390 BOOST_TEST(est.capacity(key, 1000.) == 1024u);
391
392 est.update(key, 1000., 0, 1024, false, false);
393 BOOST_TEST(est.capacity(key, 1000.) == 1024u);
394}
395
396BOOST_AUTO_TEST_CASE(estimator_converges_and_reacts_to_overflow)
397{
399 const auto key = CapacityEstimator::makeKey(SlabSite::Tracklets, 1, 0, 0);
400 constexpr double scale = 1000.;
401 constexpr double rate = 5.;
402
403 for (int tf = 0; tf < 12; ++tf) {
404 const size_t cap = est.capacity(key, scale);
405 const auto emitted = size_t(scale * rate);
406 est.update(key, scale, emitted, cap != 0 ? cap : emitted, cap != 0 && emitted > cap, false);
407 }
408
409 const size_t cap = est.capacity(key, scale);
410 BOOST_TEST(cap >= size_t(scale * rate));
411 BOOST_TEST(cap <= size_t(scale * rate * 1.35));
412
413 const size_t bigger = est.capacity(key, 2. * scale);
414 BOOST_TEST(bigger > size_t(2. * scale * rate));
415 BOOST_TEST(bigger <= size_t(2. * scale * rate * 1.35));
416
417 est.update(key, scale, size_t(scale * rate * 4.), size_t(scale * rate), true, false);
418 BOOST_TEST(est.capacity(key, scale) > cap);
419}
420
421BOOST_AUTO_TEST_CASE(estimator_does_not_extrapolate_a_low_statistics_ratio)
422{
423 // A first sample taken on a handful of inputs sets the ratio outright, so without a ceiling the
424 // next timeframe would ask for a slab orders of magnitude past anything the site ever emitted.
426 const auto key = CapacityEstimator::makeKey(SlabSite::Roads, 2, CapacityEstimator::makeVariant(3, 3), 5);
427 constexpr size_t emitted = 100000;
428
429 est.update(key, 2., emitted, est.capacity(key, 2.), true, false); // ratio of 50000, from two inputs
430
431 const size_t asked = est.capacity(key, 500000.);
432 BOOST_TEST(asked <= emitted * 4u); // bounded by what this site has ever actually produced
433 BOOST_TEST(asked >= emitted); // but still enough headroom not to force a pointless retry
434}
435
436BOOST_AUTO_TEST_CASE(estimator_reports_a_scale_independent_peak)
437{
438 // Sizing a buffer that has to serve several differently sized runs cannot use capacity(), which
439 // needs the scale of one particular run.
441 const auto key = CapacityEstimator::makeKey(SlabSite::Roads, 0, CapacityEstimator::makeVariant(5, 3), 2);
442 BOOST_TEST(est.peakCapacity(key) == 1024u); // cold start falls back to the floor
443
444 est.update(key, 1000., 50000, 60000, false, false);
445 BOOST_TEST(est.peakCapacity(key) >= 50000u);
446
447 est.update(key, 10., 700, 1024, false, false); // a much smaller run must not shrink the peak
448 BOOST_TEST(est.peakCapacity(key) >= 50000u);
449 BOOST_TEST(est.peakCapacity(key) <= 50000u * 4u);
450}
451
452BOOST_AUTO_TEST_CASE(estimator_expected_tracks_the_current_input)
453{
454 // Chaining sites whose input is the previous one's output needs a margin-free prediction that
455 // follows this timeframe, not the largest one ever seen.
457 const auto key = CapacityEstimator::makeKey(SlabSite::Roads, 0, CapacityEstimator::makeVariant(5, 4), 4);
458 BOOST_TEST(est.expected(key, 1000.) == 0.); // nothing learned yet
459
460 est.update(key, 1000., 2000, 2600, false, false); // ratio of 2
461 BOOST_TEST(est.expected(key, 1000.) == 2000.);
462 BOOST_TEST(est.expected(key, 250.) == 500.); // a smaller timeframe predicts proportionally less
463 BOOST_TEST(est.expected(key, 0.) == 0.);
464
465 // ... while the all-time peak stays where it was, which is why it cannot size a shared buffer.
466 BOOST_TEST(est.peakCapacity(key) >= 2000u);
467}
468
469BOOST_AUTO_TEST_CASE(estimator_ceiling_follows_real_growth)
470{
472 const auto key = CapacityEstimator::makeKey(SlabSite::Cells, 0, 0, 1);
473 constexpr double scale = 1000.;
474 size_t need = 10000;
475
476 for (int tf = 0; tf < 6; ++tf) {
477 const size_t cap = est.capacity(key, scale);
478 est.update(key, scale, need, cap, need > cap, false);
479 need *= 2;
480 }
481 // Each timeframe doubled the output; the ceiling has to have followed, or every one of them
482 // would have paid for a retry.
483 BOOST_TEST(est.capacity(key, scale) >= need / 2);
484}
485
486BOOST_AUTO_TEST_CASE(estimator_backs_off_when_the_pool_refuses)
487{
489 const auto key = CapacityEstimator::makeKey(SlabSite::Roads, 2, 0, 0);
490 constexpr double scale = 1000.;
491 constexpr double rate = 5.;
492 const auto emitted = size_t(scale * rate);
493
494 for (int tf = 0; tf < 12; ++tf) {
495 const size_t cap = est.capacity(key, scale);
496 est.update(key, scale, emitted, cap, emitted > cap, false);
497 }
498 const size_t settled = est.capacity(key, scale);
499
500 for (int tf = 0; tf < 12; ++tf) {
501 est.update(key, scale, emitted, 100, true, true);
502 }
503 BOOST_TEST(est.capacity(key, scale) < settled);
504}
505
506BOOST_AUTO_TEST_CASE(estimator_grows_in_proportion_to_the_miss)
507{
509 constexpr double scale = 1000.;
510 const auto nearMiss = CapacityEstimator::makeKey(SlabSite::Cells, 3, 0, 0);
511 const auto wayOff = CapacityEstimator::makeKey(SlabSite::Cells, 3, 0, 1);
512
513 for (const auto key : {nearMiss, wayOff}) {
514 est.update(key, scale, 2000, 2000, false, false);
515 }
516 const size_t settled = est.capacity(nearMiss, scale);
517
518 est.update(nearMiss, scale, 2000, 1900, true, false); // overran by 5%
519 est.update(wayOff, scale, 2000, 500, true, false); // overran by 4x
520
521 const size_t afterNearMiss = est.capacity(nearMiss, scale);
522 const size_t afterWayOff = est.capacity(wayOff, scale);
523 BOOST_TEST(afterNearMiss > settled);
524 BOOST_TEST(afterNearMiss < afterWayOff);
525 BOOST_TEST(afterNearMiss < size_t(1.25 * double(settled)));
526 BOOST_TEST(afterWayOff > size_t(1.4 * double(settled)));
527}
528
529BOOST_AUTO_TEST_CASE(estimator_recovers_from_a_single_overflow)
530{
532 cfg.decayAfter = 1;
533 CapacityEstimator est{cfg};
534 const auto key = CapacityEstimator::makeKey(SlabSite::Tracklets, 4, 0, 0);
535 constexpr double scale = 1000.;
536
537 est.update(key, scale, 2000, 2000, false, false);
538 est.update(key, scale, 2000, 500, true, false);
539 const size_t inflated = est.capacity(key, scale);
540
541 for (int tf = 0; tf < 30; ++tf) {
542 est.update(key, scale, 2000, 20000, false, false); // 10% utilisation
543 }
544 const size_t recovered = est.capacity(key, scale);
545 BOOST_TEST(recovered < inflated);
546 BOOST_TEST(recovered <= size_t(2. * scale * double(cfg.marginMin)) + 2);
547}
548
549BOOST_AUTO_TEST_CASE(estimator_decay_survives_interleaved_busy_timeframes)
550{
552 cfg.decayAfter = 4;
553 CapacityEstimator est{cfg};
554 const auto key = CapacityEstimator::makeKey(SlabSite::Roads, 5, 0, 0);
555 constexpr double scale = 1000.;
556
557 est.update(key, scale, 2000, 2000, false, false);
558 est.update(key, scale, 2000, 500, true, false);
559 const size_t inflated = est.capacity(key, scale);
560
561 for (int tf = 0; tf < 80; ++tf) {
562 const bool quiet = (tf % 4) != 3;
563 est.update(key, scale, 2000, quiet ? 20000 : 2000, false, false);
564 }
565 BOOST_TEST(est.capacity(key, scale) < inflated);
566}
567
568BOOST_AUTO_TEST_CASE(estimator_reset_forgets_inflated_margins)
569{
571 const auto key = CapacityEstimator::makeKey(SlabSite::Cells, 0, 0, 0);
572 constexpr double scale = 1000.;
573
574 for (int tf = 0; tf < 6; ++tf) {
575 est.update(key, scale, size_t(scale * 5.), 10, true, false);
576 }
577 BOOST_TEST(est.capacity(key, scale) > 5000u);
578
579 est.reset();
580 BOOST_TEST(est.capacity(key, scale) == 1024u);
581}
582
583BOOST_AUTO_TEST_CASE(estimator_keys_separate_the_road_walk_steps)
584{
585 const auto a = CapacityEstimator::makeKey(SlabSite::Roads, 0, CapacityEstimator::makeVariant(6, 4), 1);
586 const auto b = CapacityEstimator::makeKey(SlabSite::Roads, 0, CapacityEstimator::makeVariant(5, 4), 1);
587 const auto c = CapacityEstimator::makeKey(SlabSite::Roads, 0, CapacityEstimator::makeVariant(6, 4), 2);
588 BOOST_TEST(a != b);
589 BOOST_TEST(a != c);
590 BOOST_TEST(b != c);
591}
Cross-timeframe output-size prediction.
int32_t i
const int16_t bb
uint32_t c
Definition RawData.h:2
Lock-free slot allocator and single-pass sink.
benchmark::State & st
StringRef key
Class for time synchronization of RawReader instances.
size_t capacity(uint64_t key, double scale) const
void update(uint64_t key, double scale, size_t emitted, size_t capacityUsed, bool overflowed, bool memoryLimited)
static constexpr int makeVariant(int high, int low) noexcept
static constexpr KeyType makeKey(SlabSite site, int iteration, int variant, int slot) noexcept
double expected(uint64_t key, double scale) const
size_t peakCapacity(uint64_t key) const
static size_t suggestSlab(size_t capacity, int nThreads, size_t minSlab=256, size_t maxSlab=4096) noexcept
SlabSinkStats stats() const
GLdouble n
Definition glcorearb.h:1982
GLsizeiptr size
Definition glcorearb.h:659
GLuint GLenum * rate
Definition glcorearb.h:5735
const GLdouble * v
Definition glcorearb.h:832
GLdouble f
Definition glcorearb.h:310
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLint reference
Definition glcorearb.h:5487
GLboolean r
Definition glcorearb.h:1233
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
GLint ref
Definition glcorearb.h:291
BOOST_AUTO_TEST_CASE(Descriptor_test)
std::pmr::vector< T > bounded_vector
std::ostream & operator<<(std::ostream &os, Detector &source)
std::unique_ptr< GPUReconstructionTimeframe > tf
size_t capacity
slots the memory pool actually granted
std::map< std::string, ID > expected
bool operator==(const CoarseLocation &a, const CoarseLocation &b)
VectorOfTObjectPtrs other
BOOST_TEST(digits==digitsD, boost::test_tools::per_element())