12#define BOOST_TEST_MODULE Test SlabBumpAllocator
13#define BOOST_TEST_MAIN
14#define BOOST_TEST_DYN_LINK
16#include <boost/test/unit_test.hpp>
21#include <memory_resource>
27#include <oneapi/tbb/parallel_for.h>
28#include <oneapi/tbb/task_arena.h>
44 Rec(
int aa,
int bb,
float p) :
a{aa},
b{
bb}, payload{p} {}
45 bool operator<(
const Rec& o)
const
47 if ((
a < 0) != (o.a < 0)) {
50 return a != o.a ?
a < o.a :
b < o.b;
52 bool operator==(
const Rec& o)
const {
return a == o.a &&
b == o.b; }
55std::ostream&
operator<<(std::ostream& os,
const Rec&
r)
57 return os <<
"Rec{" <<
r.a <<
',' <<
r.b <<
',' <<
r.payload <<
'}';
63 explicit StingyResource(
size_t maxBytes) : mMax{maxBytes} {}
66 void* do_allocate(
size_t bytes,
size_t alignment)
final
69 throw std::bad_alloc{};
71 return std::pmr::new_delete_resource()->allocate(bytes, alignment);
73 void do_deallocate(
void* p,
size_t bytes,
size_t alignment)
final
75 std::pmr::new_delete_resource()->deallocate(p, bytes, alignment);
83void runConcurrently(F&&
f)
85 tbb::task_arena arena{4};
86 arena.execute(std::forward<F>(
f));
89template <
typename Emit>
90void produce(
int i, uint32_t seed, Emit&& emit)
92 std::mt19937 rng(seed + (uint32_t(
i) * 2654435761u));
93 const int n =
int(rng() % 12);
94 for (
int k = 0; k <
n; ++k) {
95 emit(
i, k,
float((
i * 100) + k));
99std::vector<std::vector<Rec>>
reference(
int nProducers, uint32_t seed)
101 std::vector<std::vector<Rec>> out(nProducers);
102 for (
int i = 0;
i < nProducers; ++
i) {
103 produce(
i, seed, [&](
int a,
int b,
float p) { out[
i].emplace_back(
a,
b, p); });
108struct EstimatorSnapshot {
110 size_t peakCapacity{0};
117 return {.capacity = estimator.
capacity(
key, scale),
123void checkSnapshot(
const EstimatorSnapshot& actual,
const EstimatorSnapshot&
expected)
138void checkGrouped(
int nProducers,
size_t capacity,
size_t slab,
size_t maxMemory = std::numeric_limits<size_t>::max())
140 constexpr uint32_t seed = 7u;
144 std::vector<Rec> flat;
145 std::vector<int> refLut(nProducers + 1, 0);
146 for (
int i = 0;
i < nProducers; ++
i) {
148 flat.insert(flat.end(),
ref[
i].begin(),
ref[
i].end());
152 runConcurrently([&] {
153 tbb::parallel_for(0, nProducers, [&](
int i) {
154 auto&
h = sink.local();
156 produce(
i, seed, [&](
int a,
int b,
float p) {
h.emplace(
a,
b, p); });
165 sink.finalizeGrouped(
size_t(nProducers), lut, dest);
167 BOOST_REQUIRE(lut.size() ==
size_t(nProducers) + 1);
168 BOOST_TEST(std::equal(lut.begin(), lut.end(), refLut.begin()));
169 BOOST_REQUIRE(dest.size() == flat.size());
170 for (
size_t i = 0;
i < flat.size(); ++
i) {
176void checkUnordered(
int nProducers,
size_t capacity,
size_t slab,
size_t maxMemory = std::numeric_limits<size_t>::max())
178 constexpr uint32_t seed = 11u;
182 std::vector<Rec> flat;
183 for (
const auto&
v :
ref) {
184 flat.insert(flat.end(),
v.begin(),
v.end());
186 std::sort(flat.begin(), flat.end());
187 flat.erase(std::unique(flat.begin(), flat.end()), flat.end());
190 runConcurrently([&] {
191 tbb::parallel_for(0, nProducers, [&](
int i) {
192 auto&
h = sink.local();
193 produce(
i, seed, [&](
int a,
int b,
float p) {
h.emplace(
a,
b, p); });
201 sink.finalizeUnordered(dest);
203 std::sort(dest.begin(), dest.end());
205 BOOST_REQUIRE(dest.size() == flat.size());
206 for (
size_t i = 0;
i < flat.size(); ++
i) {
217 std::vector<char> seen(1000, 0);
220 const auto r = alloc.grab();
224 BOOST_REQUIRE(
r.base +
r.n <= 1000);
225 for (
size_t s =
r.base; s <
r.base +
r.n; ++s) {
226 BOOST_REQUIRE(seen[s] == 0);
244 checkGrouped(2000, 40000, 512);
245 checkGrouped(300, 20000, 4096);
250 checkGrouped(2000, 3000, 256);
251 checkGrouped(500, 0, 1, 1u << 20);
256 checkGrouped(20, 1u << 20, 256, 1u << 16);
262 const std::vector<int> counts{3, 5, 6, 0, 2};
265 auto&
h = sink.local();
266 for (
size_t p = 0; p < counts.size(); ++p) {
267 h.beginProducer(
int(p));
268 for (
int k = 0; k < counts[p]; ++k) {
269 h.emplace(
int(p), k,
float(k));
272 const auto st = sink.stats();
279 sink.finalizeGrouped(counts.size(), lut, dest);
281 BOOST_REQUIRE(lut.size() == counts.size() + 1);
282 BOOST_REQUIRE(dest.size() == 16u);
284 for (
size_t p = 0; p < counts.size(); ++p) {
286 for (
int k = 0; k < counts[p]; ++k) {
296 checkUnordered(2000, 40000, 512);
297 checkUnordered(300, 20000, 4096);
302 checkUnordered(2000, 3000, 256);
303 checkUnordered(500, 0, 1, 1u << 20);
311 auto&
h = sink.local();
312 for (
int i = 0;
i < 14; ++
i) {
313 h.emplace(
i,
i + 1,
float(
i));
315 const auto st = sink.stats();
320 sink.finalizeUnordered(dest);
322 BOOST_REQUIRE(dest.size() == 14u);
323 for (
int i = 0;
i < 14; ++
i) {
332 sink.local().emplace(1, 2, 3.f);
333 sink.local().emplace();
336 sink.finalizeUnordered(dest);
338 BOOST_REQUIRE(dest.size() == 2u);
349 auto&
h = sink.local();
350 for (
int i = 0;
i < 100; ++
i) {
351 h.emplace(
i,
i + 1,
float(
i));
354 sink.finalizeUnordered(dest);
356 BOOST_REQUIRE(dest.size() == 100u);
362 constexpr size_t maxMemory = 1u << 16;
376 size_t alone{0}, shared{0};
394 StingyResource mr{1u << 12};
401 auto& handle = sink.local();
402 for (
int i = 0;
i < 10; ++
i) {
403 handle.emplace(
i,
i + 1,
float(
i));
407 sink.finalizeUnordered(dest);
408 BOOST_REQUIRE(dest.size() == 10u);
409 for (
int i = 0;
i < 10; ++
i) {
424 est.
update(
key, 1000., 0, 1024,
false,
false);
432 constexpr double scale = 1000.;
433 constexpr double rate = 5.;
435 for (
int tf = 0;
tf < 12; ++
tf) {
437 const auto emitted = size_t(scale *
rate);
438 est.
update(
key, scale, emitted, cap != 0 ? cap : emitted, cap != 0 && emitted > cap,
false);
445 const size_t bigger = est.
capacity(
key, 2. * scale);
449 est.
update(
key, scale,
size_t(scale *
rate * 4.),
size_t(scale *
rate),
true,
false);
459 constexpr size_t emitted = 100000;
476 est.
update(
key, 1000., 50000, 60000,
false,
false);
479 est.
update(
key, 10., 700, 1024,
false,
false);
492 est.
update(
key, 1000., 2000, 2600,
false,
false);
505 constexpr double scale = 1000.;
508 for (
int tf = 0;
tf < 6; ++
tf) {
510 est.
update(
key, scale, need, cap, need > cap,
false);
522 constexpr double scale = 1000.;
523 constexpr double rate = 5.;
524 const auto emitted = size_t(scale *
rate);
526 for (
int tf = 0;
tf < 12; ++
tf) {
528 est.
update(
key, scale, emitted, cap, emitted > cap,
false);
532 for (
int tf = 0;
tf < 12; ++
tf) {
533 est.
update(
key, scale, emitted, 100,
true,
true);
541 constexpr double scale = 1000.;
545 for (
const auto key : {nearMiss, wayOff}) {
546 est.
update(
key, scale, 2000, 2000,
false,
false);
548 const size_t settled = est.
capacity(nearMiss, scale);
550 est.
update(nearMiss, scale, 2000, 1900,
true,
false);
551 est.
update(wayOff, scale, 2000, 500,
true,
false);
553 const size_t afterNearMiss = est.
capacity(nearMiss, scale);
554 const size_t afterWayOff = est.
capacity(wayOff, scale);
557 BOOST_TEST(afterNearMiss <
size_t(1.25 *
double(settled)));
558 BOOST_TEST(afterWayOff >
size_t(1.4 *
double(settled)));
567 constexpr double scale = 1000.;
569 est.update(
key, scale, 2000, 2000,
false,
false);
570 est.update(
key, scale, 2000, 500,
true,
false);
571 const size_t inflated = est.capacity(
key, scale);
573 for (
int tf = 0;
tf < 30; ++
tf) {
574 est.update(
key, scale, 2000, 20000,
false,
false);
576 const size_t recovered = est.capacity(
key, scale);
587 constexpr double scale = 1000.;
589 est.update(
key, scale, 2000, 2000,
false,
false);
590 est.update(
key, scale, 2000, 500,
true,
false);
591 const size_t inflated = est.capacity(
key, scale);
593 for (
int tf = 0;
tf < 80; ++
tf) {
594 const bool quiet = (
tf % 4) != 3;
595 est.update(
key, scale, 2000, quiet ? 20000 : 2000,
false,
false);
604 constexpr double scale = 1000.;
606 for (
int tf = 0;
tf < 6; ++
tf) {
607 est.
update(
key, scale,
size_t(scale * 5.), 10,
true,
false);
619 est.
update(
key, 100., 120, 100, 95, 7,
true,
false);
631 est.
update(
key, 100., 80, 80, 70, 0,
false,
false);
632 const auto beforeCommit = snapshot(est,
key, 100.);
634 checkSnapshot(snapshot(est,
key, 100.), beforeCommit);
641 constexpr double scale = 100.;
642 est.
update(
key, scale, 120, 100, 95, 7,
true,
false);
643 const auto before = snapshot(est,
key, scale);
646 est.
update(
key, scale, 8000, 6000, 5500, 500,
true,
false);
647 est.
update(
key, scale, 40, 400, 20, 0,
false,
false);
648 const auto during = snapshot(est,
key, scale);
649 BOOST_TEST(during.statistics.samples == before.statistics.samples + 2u);
650 BOOST_TEST(during.statistics.requested == before.statistics.requested + 8040u);
651 BOOST_TEST(during.peakCapacity > before.peakCapacity);
654 checkSnapshot(snapshot(est,
key, scale), before);
661 constexpr double scale = 50.;
662 const auto absent = snapshot(est,
key, scale);
665 est.
update(
key, scale, 90, 80, 75, 4,
true,
false);
670 checkSnapshot(snapshot(est,
key, scale), absent);
677 constexpr double scale = 100.;
678 est.
update(
key, scale, 50, 50, 40, 0,
false,
false);
679 const auto before = snapshot(est,
key, scale);
682 est.
update(
key, scale, 200, 180, 160, 5,
true,
false);
683 const auto beforeRejectedBegin = snapshot(est,
key, scale);
685 checkSnapshot(snapshot(est,
key, scale), beforeRejectedBegin);
687 checkSnapshot(snapshot(est,
key, scale), before);
698 constexpr double scale = 100.;
699 est.
update(existing, scale, 200, 180, 170, 3,
true,
false);
701 est.
update(existing, scale, 300, 250, 240, 5,
true,
false);
702 est.
update(created, scale, 100, 90, 80, 2,
true,
false);
710 est.
update(existing, scale, 60, 60, 50, 0,
false,
false);
Cross-timeframe output-size prediction.
std::ostream & operator<<(std::ostream &os, const o2::math_utils::Rotation2Df_t &t)
Lock-free slot allocator and single-pass sink.
Class for time synchronization of RawReader instances.
static constexpr int makeVariant(int high, int low) noexcept
void commitTransaction() noexcept
static constexpr KeyType makeKey(SlabSite site, int iteration, int variant, int slot) noexcept
void rollbackTransaction() noexcept
Statistics statistics(uint64_t key) const
size_t capacity(uint64_t key, double scale) const
size_t peakCapacity(uint64_t key) const
double expected(uint64_t key, double scale) const
void update(uint64_t key, double scale, size_t emitted, size_t capacityUsed, bool overflowed, bool memoryLimited)
static size_t suggestSlab(size_t capacity, int nThreads, size_t minSlab=256, size_t maxSlab=4096) noexcept
SlabSinkStats stats() const
GLboolean GLboolean GLboolean b
GLboolean GLboolean GLboolean GLboolean a
std::pmr::vector< T > bounded_vector
std::unique_ptr< GPUReconstructionTimeframe > tf
size_t capacity
slots the memory pool actually granted
std::map< std::string, ID > expected
BOOST_AUTO_TEST_CASE(slab_hands_out_disjoint_ranges)
BOOST_CHECK_NO_THROW(algorithm::merge(target, other))
VectorOfTObjectPtrs other
BOOST_TEST(digits==digitsD, boost::test_tools::per_element())