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>
26#include <oneapi/tbb/task_arena.h>
42 Rec(
int aa,
int bb,
float p) :
a{aa},
b{
bb}, payload{p} {}
43 bool operator<(
const Rec& o)
const
45 if ((
a < 0) != (o.a < 0)) {
48 return a != o.a ?
a < o.a :
b < o.b;
50 bool operator==(
const Rec& o)
const {
return a == o.a &&
b == o.b; }
53std::ostream&
operator<<(std::ostream& os,
const Rec&
r)
55 return os <<
"Rec{" <<
r.a <<
',' <<
r.b <<
',' <<
r.payload <<
'}';
61 explicit StingyResource(
size_t maxBytes) : mMax{maxBytes} {}
64 void* do_allocate(
size_t bytes,
size_t alignment)
final
67 throw std::bad_alloc{};
69 return std::pmr::new_delete_resource()->allocate(bytes, alignment);
71 void do_deallocate(
void* p,
size_t bytes,
size_t alignment)
final
73 std::pmr::new_delete_resource()->deallocate(p, bytes, alignment);
81void runConcurrently(F&&
f)
83 tbb::task_arena arena{4};
84 arena.execute(std::forward<F>(
f));
87template <
typename Emit>
88void produce(
int i, uint32_t seed, Emit&& emit)
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));
97std::vector<std::vector<Rec>>
reference(
int nProducers, uint32_t seed)
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); });
106void checkGrouped(
int nProducers,
size_t capacity,
size_t slab,
size_t maxMemory = std::numeric_limits<size_t>::max())
108 constexpr uint32_t seed = 7u;
112 std::vector<Rec> flat;
113 std::vector<int> refLut(nProducers + 1, 0);
114 for (
int i = 0;
i < nProducers; ++
i) {
116 flat.insert(flat.end(),
ref[
i].begin(),
ref[
i].end());
120 runConcurrently([&] {
121 tbb::parallel_for(0, nProducers, [&](
int i) {
122 auto&
h = sink.local();
124 produce(
i, seed, [&](
int a,
int b,
float p) {
h.emplace(
a,
b, p); });
133 sink.finalizeGrouped(
size_t(nProducers), lut, dest);
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) {
144void checkUnordered(
int nProducers,
size_t capacity,
size_t slab,
size_t maxMemory = std::numeric_limits<size_t>::max())
146 constexpr uint32_t seed = 11u;
150 std::vector<Rec> flat;
151 for (
const auto&
v :
ref) {
152 flat.insert(flat.end(),
v.begin(),
v.end());
154 std::sort(flat.begin(), flat.end());
155 flat.erase(std::unique(flat.begin(), flat.end()), flat.end());
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); });
169 sink.finalizeUnordered(dest);
171 std::sort(dest.begin(), dest.end());
173 BOOST_REQUIRE(dest.size() == flat.size());
174 for (
size_t i = 0;
i < flat.size(); ++
i) {
185 std::vector<char> seen(1000, 0);
188 const auto r = alloc.grab();
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);
212 checkGrouped(2000, 40000, 512);
213 checkGrouped(300, 20000, 4096);
218 checkGrouped(2000, 3000, 256);
219 checkGrouped(500, 0, 1, 1u << 20);
224 checkGrouped(20, 1u << 20, 256, 1u << 16);
230 const std::vector<int> counts{3, 5, 6, 0, 2};
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));
240 const auto st = sink.stats();
247 sink.finalizeGrouped(counts.size(), lut, dest);
249 BOOST_REQUIRE(lut.size() == counts.size() + 1);
250 BOOST_REQUIRE(dest.size() == 16u);
252 for (
size_t p = 0; p < counts.size(); ++p) {
254 for (
int k = 0; k < counts[p]; ++k) {
264 checkUnordered(2000, 40000, 512);
265 checkUnordered(300, 20000, 4096);
270 checkUnordered(2000, 3000, 256);
271 checkUnordered(500, 0, 1, 1u << 20);
279 auto&
h = sink.local();
280 for (
int i = 0;
i < 14; ++
i) {
281 h.emplace(
i,
i + 1,
float(
i));
283 const auto st = sink.stats();
288 sink.finalizeUnordered(dest);
290 BOOST_REQUIRE(dest.size() == 14u);
291 for (
int i = 0;
i < 14; ++
i) {
300 sink.local().emplace(1, 2, 3.f);
301 sink.local().emplace();
304 sink.finalizeUnordered(dest);
306 BOOST_REQUIRE(dest.size() == 2u);
317 auto&
h = sink.local();
318 for (
int i = 0;
i < 100; ++
i) {
319 h.emplace(
i,
i + 1,
float(
i));
322 sink.finalizeUnordered(dest);
324 BOOST_REQUIRE(dest.size() == 100u);
330 constexpr size_t maxMemory = 1u << 16;
344 size_t alone{0}, shared{0};
362 StingyResource mr{1u << 12};
369 auto& handle = sink.local();
370 for (
int i = 0;
i < 10; ++
i) {
371 handle.emplace(
i,
i + 1,
float(
i));
375 sink.finalizeUnordered(dest);
376 BOOST_REQUIRE(dest.size() == 10u);
377 for (
int i = 0;
i < 10; ++
i) {
392 est.
update(
key, 1000., 0, 1024,
false,
false);
400 constexpr double scale = 1000.;
401 constexpr double rate = 5.;
403 for (
int tf = 0;
tf < 12; ++
tf) {
405 const auto emitted = size_t(scale *
rate);
406 est.
update(
key, scale, emitted, cap != 0 ? cap : emitted, cap != 0 && emitted > cap,
false);
413 const size_t bigger = est.
capacity(
key, 2. * scale);
417 est.
update(
key, scale,
size_t(scale *
rate * 4.),
size_t(scale *
rate),
true,
false);
427 constexpr size_t emitted = 100000;
444 est.
update(
key, 1000., 50000, 60000,
false,
false);
447 est.
update(
key, 10., 700, 1024,
false,
false);
460 est.
update(
key, 1000., 2000, 2600,
false,
false);
473 constexpr double scale = 1000.;
476 for (
int tf = 0;
tf < 6; ++
tf) {
478 est.
update(
key, scale, need, cap, need > cap,
false);
490 constexpr double scale = 1000.;
491 constexpr double rate = 5.;
492 const auto emitted = size_t(scale *
rate);
494 for (
int tf = 0;
tf < 12; ++
tf) {
496 est.
update(
key, scale, emitted, cap, emitted > cap,
false);
500 for (
int tf = 0;
tf < 12; ++
tf) {
501 est.
update(
key, scale, emitted, 100,
true,
true);
509 constexpr double scale = 1000.;
513 for (
const auto key : {nearMiss, wayOff}) {
514 est.
update(
key, scale, 2000, 2000,
false,
false);
516 const size_t settled = est.
capacity(nearMiss, scale);
518 est.
update(nearMiss, scale, 2000, 1900,
true,
false);
519 est.
update(wayOff, scale, 2000, 500,
true,
false);
521 const size_t afterNearMiss = est.
capacity(nearMiss, scale);
522 const size_t afterWayOff = est.
capacity(wayOff, scale);
525 BOOST_TEST(afterNearMiss <
size_t(1.25 *
double(settled)));
526 BOOST_TEST(afterWayOff >
size_t(1.4 *
double(settled)));
535 constexpr double scale = 1000.;
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);
541 for (
int tf = 0;
tf < 30; ++
tf) {
542 est.update(
key, scale, 2000, 20000,
false,
false);
544 const size_t recovered = est.capacity(
key, scale);
555 constexpr double scale = 1000.;
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);
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);
572 constexpr double scale = 1000.;
574 for (
int tf = 0;
tf < 6; ++
tf) {
575 est.
update(
key, scale,
size_t(scale * 5.), 10,
true,
false);
Cross-timeframe output-size prediction.
Lock-free slot allocator and single-pass sink.
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
GLboolean GLboolean GLboolean b
GLboolean GLboolean GLboolean GLboolean a
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
VectorOfTObjectPtrs other
BOOST_TEST(digits==digitsD, boost::test_tools::per_element())