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>
45 Rec(
int aa,
int bb,
float p) :
a{aa},
b{
bb}, payload{p} {}
46 bool operator<(
const Rec& o)
const
48 if ((
a < 0) != (o.a < 0)) {
51 return a != o.a ?
a < o.a :
b < o.b;
53 bool operator==(
const Rec& o)
const {
return a == o.a &&
b == o.b; }
56std::ostream&
operator<<(std::ostream& os,
const Rec&
r)
58 return os <<
"Rec{" <<
r.a <<
',' <<
r.b <<
',' <<
r.payload <<
'}';
64 explicit StingyResource(
size_t maxBytes) : mMax{maxBytes} {}
67 void* do_allocate(
size_t bytes,
size_t alignment)
final
70 throw std::bad_alloc{};
72 return std::pmr::new_delete_resource()->allocate(bytes, alignment);
74 void do_deallocate(
void* p,
size_t bytes,
size_t alignment)
final
76 std::pmr::new_delete_resource()->deallocate(p, bytes, alignment);
84void runConcurrently(F&&
f)
86 tbb::task_arena arena{4};
87 arena.execute(std::forward<F>(
f));
90template <
typename Emit>
91void produce(
int i, uint32_t seed, Emit&& emit)
93 std::mt19937 rng(seed + (uint32_t(
i) * 2654435761u));
94 const int n = int(rng() % 12);
95 for (
int k = 0; k <
n; ++k) {
96 emit(
i, k,
float((
i * 100) + k));
100std::vector<std::vector<Rec>>
reference(
int nProducers, uint32_t seed)
102 std::vector<std::vector<Rec>> out(nProducers);
103 for (
int i = 0;
i < nProducers; ++
i) {
104 produce(
i, seed, [&](
int a,
int b,
float p) { out[
i].emplace_back(
a,
b, p); });
109struct EstimatorSnapshot {
111 size_t peakCapacity{0};
118 return {.capacity = estimator.
capacity(
key, scale),
124void checkSnapshot(
const EstimatorSnapshot& actual,
const EstimatorSnapshot&
expected)
139void checkGrouped(
int nProducers,
size_t capacity,
size_t slab,
size_t maxMemory = std::numeric_limits<size_t>::max())
141 constexpr uint32_t seed = 7u;
145 std::vector<Rec> flat;
146 std::vector<int> refLut(nProducers + 1, 0);
147 for (
int i = 0;
i < nProducers; ++
i) {
148 refLut[
i + 1] = refLut[
i] + int(
ref[
i].
size());
149 flat.insert(flat.end(),
ref[
i].begin(),
ref[
i].end());
153 runConcurrently([&] {
154 tbb::parallel_for(0, nProducers, [&](
int i) {
155 auto&
h = sink.local();
157 produce(
i, seed, [&](
int a,
int b,
float p) {
h.emplace(
a,
b, p); });
166 sink.finalizeGrouped(
size_t(nProducers), lut, dest);
168 BOOST_REQUIRE(lut.size() ==
size_t(nProducers) + 1);
169 BOOST_TEST(std::equal(lut.begin(), lut.end(), refLut.begin()));
170 BOOST_REQUIRE(dest.size() == flat.size());
171 for (
size_t i = 0;
i < flat.size(); ++
i) {
177void checkUnordered(
int nProducers,
size_t capacity,
size_t slab,
size_t maxMemory = std::numeric_limits<size_t>::max())
179 constexpr uint32_t seed = 11u;
183 std::vector<Rec> flat;
184 for (
const auto&
v :
ref) {
185 flat.insert(flat.end(),
v.begin(),
v.end());
187 std::sort(flat.begin(), flat.end());
188 flat.erase(std::unique(flat.begin(), flat.end()), flat.end());
191 runConcurrently([&] {
192 tbb::parallel_for(0, nProducers, [&](
int i) {
193 auto&
h = sink.local();
194 produce(
i, seed, [&](
int a,
int b,
float p) {
h.emplace(
a,
b, p); });
202 sink.finalizeUnordered(dest);
204 std::sort(dest.begin(), dest.end());
206 BOOST_REQUIRE(dest.size() == flat.size());
207 for (
size_t i = 0;
i < flat.size(); ++
i) {
218 std::vector<char> seen(1000, 0);
221 const auto r = alloc.grab();
225 BOOST_REQUIRE(
r.base +
r.n <= 1000);
226 for (
size_t s =
r.base; s <
r.base +
r.n; ++s) {
227 BOOST_REQUIRE(seen[s] == 0);
245 checkGrouped(2000, 40000, 512);
246 checkGrouped(300, 20000, 4096);
251 checkGrouped(2000, 3000, 256);
252 checkGrouped(500, 0, 1, 1u << 20);
257 checkGrouped(20, 1u << 20, 256, 1u << 16);
263 const std::vector<int> counts{3, 5, 6, 0, 2};
266 auto&
h = sink.local();
267 for (
size_t p = 0; p < counts.size(); ++p) {
268 h.beginProducer(
int(p));
269 for (
int k = 0; k < counts[p]; ++k) {
270 h.emplace(
int(p), k,
float(k));
273 const auto st = sink.stats();
280 sink.finalizeGrouped(counts.size(), lut, dest);
282 BOOST_REQUIRE(lut.size() == counts.size() + 1);
283 BOOST_REQUIRE(dest.size() == 16u);
285 for (
size_t p = 0; p < counts.size(); ++p) {
287 for (
int k = 0; k < counts[p]; ++k) {
297 checkUnordered(2000, 40000, 512);
298 checkUnordered(300, 20000, 4096);
303 checkUnordered(2000, 3000, 256);
304 checkUnordered(500, 0, 1, 1u << 20);
312 auto&
h = sink.local();
313 for (
int i = 0;
i < 14; ++
i) {
314 h.emplace(
i,
i + 1,
float(
i));
316 const auto st = sink.stats();
321 sink.finalizeUnordered(dest);
323 BOOST_REQUIRE(dest.size() == 14u);
324 for (
int i = 0;
i < 14; ++
i) {
333 sink.local().emplace(1, 2, 3.f);
334 sink.local().emplace();
337 sink.finalizeUnordered(dest);
339 BOOST_REQUIRE(dest.size() == 2u);
350 auto&
h = sink.local();
351 for (
int i = 0;
i < 100; ++
i) {
352 h.emplace(
i,
i + 1,
float(
i));
355 sink.finalizeUnordered(dest);
357 BOOST_REQUIRE(dest.size() == 100u);
363 constexpr size_t maxMemory = 1u << 16;
377 size_t alone{0}, shared{0};
395 StingyResource mr{1u << 12};
402 auto& handle = sink.local();
403 for (
int i = 0;
i < 10; ++
i) {
404 handle.emplace(
i,
i + 1,
float(
i));
408 sink.finalizeUnordered(dest);
409 BOOST_REQUIRE(dest.size() == 10u);
410 for (
int i = 0;
i < 10; ++
i) {
425 est.
update(
key, 1000., 0, 1024,
false,
false);
433 constexpr double scale = 1000.;
434 constexpr double rate = 5.;
436 for (
int tf = 0;
tf < 12; ++
tf) {
438 const auto emitted = size_t(scale *
rate);
439 est.
update(
key, scale, emitted, cap != 0 ? cap : emitted, cap != 0 && emitted > cap,
false);
446 const size_t bigger = est.
capacity(
key, 2. * scale);
450 est.
update(
key, scale,
size_t(scale *
rate * 4.),
size_t(scale *
rate),
true,
false);
460 constexpr size_t emitted = 100000;
477 est.
update(
key, 1000., 50000, 60000,
false,
false);
480 est.
update(
key, 10., 700, 1024,
false,
false);
493 est.
update(
key, 1000., 2000, 2600,
false,
false);
506 constexpr double scale = 1000.;
509 for (
int tf = 0;
tf < 6; ++
tf) {
511 est.
update(
key, scale, need, cap, need > cap,
false);
523 constexpr double scale = 1000.;
524 constexpr double rate = 5.;
525 const auto emitted = size_t(scale *
rate);
527 for (
int tf = 0;
tf < 12; ++
tf) {
529 est.
update(
key, scale, emitted, cap, emitted > cap,
false);
533 for (
int tf = 0;
tf < 12; ++
tf) {
534 est.
update(
key, scale, emitted, 100,
true,
true);
542 constexpr double scale = 1000.;
546 for (
const auto key : {nearMiss, wayOff}) {
547 est.
update(
key, scale, 2000, 2000,
false,
false);
549 const size_t settled = est.
capacity(nearMiss, scale);
551 est.
update(nearMiss, scale, 2000, 1900,
true,
false);
552 est.
update(wayOff, scale, 2000, 500,
true,
false);
554 const size_t afterNearMiss = est.
capacity(nearMiss, scale);
555 const size_t afterWayOff = est.
capacity(wayOff, scale);
558 BOOST_TEST(afterNearMiss <
size_t(1.25 *
double(settled)));
559 BOOST_TEST(afterWayOff >
size_t(1.4 *
double(settled)));
568 constexpr double scale = 1000.;
570 est.update(
key, scale, 2000, 2000,
false,
false);
571 est.update(
key, scale, 2000, 500,
true,
false);
572 const size_t inflated = est.capacity(
key, scale);
574 for (
int tf = 0;
tf < 30; ++
tf) {
575 est.update(
key, scale, 2000, 20000,
false,
false);
577 const size_t recovered = est.capacity(
key, scale);
588 constexpr double scale = 1000.;
590 est.update(
key, scale, 2000, 2000,
false,
false);
591 est.update(
key, scale, 2000, 500,
true,
false);
592 const size_t inflated = est.capacity(
key, scale);
594 for (
int tf = 0;
tf < 80; ++
tf) {
595 const bool quiet = (
tf % 4) != 3;
596 est.update(
key, scale, 2000, quiet ? 20000 : 2000,
false,
false);
605 constexpr double scale = 1000.;
607 for (
int tf = 0;
tf < 6; ++
tf) {
608 est.
update(
key, scale,
size_t(scale * 5.), 10,
true,
false);
620 est.
update(
key, 100., 120, 100, 95, 7,
true,
false);
632 est.
update(
key, 100., 80, 80, 70, 0,
false,
false);
633 const auto beforeCommit = snapshot(est,
key, 100.);
635 checkSnapshot(snapshot(est,
key, 100.), beforeCommit);
642 constexpr double scale = 100.;
643 est.
update(
key, scale, 120, 100, 95, 7,
true,
false);
644 const auto before = snapshot(est,
key, scale);
647 est.
update(
key, scale, 8000, 6000, 5500, 500,
true,
false);
648 est.
update(
key, scale, 40, 400, 20, 0,
false,
false);
649 const auto during = snapshot(est,
key, scale);
650 BOOST_TEST(during.statistics.samples == before.statistics.samples + 2u);
651 BOOST_TEST(during.statistics.requested == before.statistics.requested + 8040u);
652 BOOST_TEST(during.peakCapacity > before.peakCapacity);
655 checkSnapshot(snapshot(est,
key, scale), before);
662 constexpr double scale = 50.;
663 const auto absent = snapshot(est,
key, scale);
666 est.
update(
key, scale, 90, 80, 75, 4,
true,
false);
671 checkSnapshot(snapshot(est,
key, scale), absent);
678 constexpr double scale = 100.;
679 est.
update(
key, scale, 50, 50, 40, 0,
false,
false);
680 const auto before = snapshot(est,
key, scale);
683 est.
update(
key, scale, 200, 180, 160, 5,
true,
false);
684 const auto beforeRejectedBegin = snapshot(est,
key, scale);
686 checkSnapshot(snapshot(est,
key, scale), beforeRejectedBegin);
688 checkSnapshot(snapshot(est,
key, scale), before);
699 constexpr double scale = 100.;
700 est.
update(existing, scale, 200, 180, 170, 3,
true,
false);
702 est.
update(existing, scale, 300, 250, 240, 5,
true,
false);
703 est.
update(created, scale, 100, 90, 80, 2,
true,
false);
711 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())