52 using Extent = BlockFlow::Extent;
55 const size_t EXTENT_SIZ = Extent::SIZ();
57 const size_t AVERAGE_EPOCHS =
Strategy{}.averageEpochs();
59 const double TARGET_FILL =
Strategy{}.config().TARGET_FILL;
60 const double ACTIVITIES_P_FR =
Strategy{}.config().ACTIVITIES_PER_FRAME;
96 Time deadline = randTime();
100 CHECK (1 == watch(bFlow).cntElm());
101 CHECK (1 == watch(bFlow).cntEpochs());
102 CHECK (watch(bFlow).first() > deadline);
103 CHECK (watch(bFlow).first() - deadline == bFlow.getEpochStep());
106 CHECK (0 == watch(bFlow).cntEpochs());
107 CHECK (0 == watch(bFlow).cntElm());
131 Extent& extent = *alloc.begin();
132 CHECK (extent.size() == Extent::SIZ::value);
133 CHECK (
sizeof(extent) == extent.size() *
sizeof(
Activity));
134 CHECK (showType<Extent::value_type>() ==
"vault::gear::Activity"_expect);
137 extent[55].data_.feed.one = 555555555555555;
143 CHECK (epoch[55].data_.feed.one == 555555555555555);
146 Epoch::EpochGate& gate = epoch.gate();
147 CHECK (isSameObject (gate, epoch[0]));
148 CHECK (isSameObject (epoch[0], extent[0]));
149 CHECK (
Time{gate.deadline()} ==
Time(0,10));
150 CHECK (
Time{gate.deadline()} ==
Time{epoch[0].data_.condition.dead});
154 CHECK (isSameObject (*gate.next, epoch[extent.size()-1]));
155 CHECK (0 == gate.filledSlots());
156 CHECK (0 == epoch.getFillFactor());
159 epoch[extent.size()-1].data_.timing.instant =
Time{5,5};
165 CHECK (isSameObject (timeStart, epoch[extent.size()-1]));
168 CHECK (epoch[extent.size()-1].data_.timing.instant !=
Time(5,5));
169 CHECK (epoch[extent.size()-1].data_.timing.instant ==
Time::NEVER);
171 CHECK (timeStart.data_.timing.instant ==
Time::NEVER);
172 CHECK (timeStart.data_.timing.quality == 0);
175 CHECK (isSameObject (*gate.next, epoch[extent.size()-2]));
178 CHECK (1 == gate.filledSlots());
179 CHECK (gate.hasFreeSlot());
181 CHECK (epoch.getFillFactor() == double(gate.filledSlots()) / (EXTENT_SIZ-1));
184 for (uint i=extent.size()-2; i>1; --i)
185 gate.claimNextSlot();
188 CHECK (isSameObject (*gate.next, epoch[1]));
189 CHECK (gate.filledSlots() == EXTENT_SIZ-2);
190 CHECK (gate.hasFreeSlot());
192 gate.claimNextSlot();
194 CHECK (not gate.hasFreeSlot());
195 CHECK (isSameObject (*gate.next, epoch[0]));
196 CHECK (gate.filledSlots() == EXTENT_SIZ-1);
197 CHECK (epoch.getFillFactor() == 1);
200 CHECK (gate.deadline() ==
Time(0,10));
202 CHECK ( gate.isAlive (
Time(0,5)));
203 CHECK ( gate.isAlive (
Time(999,9)));
204 CHECK (not gate.isAlive (
Time(0,10)));
205 CHECK (not gate.isAlive (
Time(1,10)));
231 CHECK (watch(bFlow).allEpochs() ==
"10s200ms"_expect);
232 CHECK (watch(bFlow).find(a1) ==
"10s200ms"_expect);
236 CHECK (watch(bFlow).allEpochs() ==
"10s200ms|10s400ms|10s600ms|10s800ms|11s"_expect);
237 CHECK (watch(bFlow).find(a3) ==
"11s"_expect);
241 CHECK (watch(bFlow).allEpochs() ==
"10s200ms|10s400ms|10s600ms|10s800ms|11s"_expect);
242 CHECK (watch(bFlow).find(a2) ==
"10s600ms"_expect);
247 CHECK (watch(bFlow).allEpochs() ==
"10s200ms|10s400ms|10s600ms|10s800ms|11s"_expect);
248 CHECK (watch(bFlow).find(a0) ==
"10s200ms"_expect);
252 for (uint i=1; i<EXTENT_SIZ; ++i)
255 CHECK (allocHandle.currDeadline() ==
Time(400,10));
256 CHECK (not allocHandle.hasFreeSlot());
259 auto& a4 = allocHandle.
create();
260 CHECK (allocHandle.currDeadline() ==
Time(600,10));
261 CHECK (allocHandle.hasFreeSlot());
262 CHECK (watch(bFlow).find(a4) ==
"10s600ms"_expect);
265 for (uint i=1; i<EXTENT_SIZ; ++i)
269 CHECK (allocHandle.currDeadline() ==
Time(800,10));
270 CHECK (allocHandle.hasFreeSlot());
273 auto& a5 = bFlow.
until(
Time{220,10}).create();
274 CHECK (watch(bFlow).find(a5) ==
"10s800ms"_expect);
278 for (uint i=2; i<EXTENT_SIZ; ++i)
281 CHECK (allocHandle.currDeadline() ==
Time(0,11));
282 CHECK (not allocHandle.hasFreeSlot());
283 auto& a6 = bFlow.
until(
Time{850,10}).create();
285 CHECK (watch(bFlow).find(a6) ==
"11s192ms"_expect);
286 CHECK (watch(bFlow).allEpochs() ==
"10s200ms|10s400ms|10s600ms|10s800ms|11s|11s192ms"_expect);
288 auto& a7 = bFlow.
until(
Time{500,11}).create();
290 CHECK (watch(bFlow).allEpochs() ==
"10s200ms|10s400ms|10s600ms|10s800ms|11s|11s192ms|11s384ms|11s576ms"_expect);
291 CHECK (watch(bFlow).find(a7) ==
"11s576ms"_expect);
294 CHECK (watch(bFlow).cntElm() == 8 + EXTENT_SIZ-1 + EXTENT_SIZ-1 + EXTENT_SIZ-2);
297 CHECK (bFlow.getEpochStep() ==
"≺192ms≻"_expect);
299 CHECK (bFlow.getEpochStep() ==
"≺218ms≻"_expect);
300 CHECK (watch(bFlow).allEpochs() ==
"11s|11s192ms|11s384ms|11s576ms"_expect);
303 auto& a8 = bFlow.
until(
Time{500,10}).create();
304 CHECK (watch(bFlow).find(a8) ==
"11s192ms"_expect);
318 CHECK (bFlow.getEpochStep() == INITIAL_EPOCH_STEP);
322 CHECK (bFlow.getEpochStep() == INITIAL_EPOCH_STEP * BOOST_OVERFLOW);
324 CHECK (bFlow.getEpochStep() == INITIAL_EPOCH_STEP * BOOST_OVERFLOW*BOOST_OVERFLOW);
330 TimeVar dur1 = INITIAL_EPOCH_STEP;
332 TimeVar dur2 = INITIAL_EPOCH_STEP * BOOST_OVERFLOW;
335 double goal1 = double(_raw(dur1)) / (fac1/TARGET_FILL);
336 double goal2 = double(_raw(dur2)) / (fac2/TARGET_FILL);
338 auto movingAverage = [&](
TimeValue old,
double contribution)
340 auto N = AVERAGE_EPOCHS;
341 auto averageTicks = double(_raw(old))*(N-1)/N + contribution/N;
342 return TimeValue{gavl_time_t (floor (averageTicks))};
345 TimeVar step = bFlow.getEpochStep();
347 CHECK (bFlow.getEpochStep() == movingAverage(step, goal1));
349 step = bFlow.getEpochStep();
351 CHECK (bFlow.getEpochStep() == movingAverage(step, goal2));
362 Duration initialStep{bFlow.getEpochStep()};
363 size_t initialFPS = Strategy{}.initialFrameRate();
367 CHECK (bFlow.getEpochStep() * 2 == initialStep);
371 CHECK (bFlow.getEpochStep() * 4 == initialStep);
397 const size_t FPS = 200;
398 const size_t TICK_P_S = FPS * ACTIVITIES_P_FR;
400 const gavl_time_t RUN = _raw(
Time{0,0,3});
401 Offset BASE_DEADLINE{FSecs{1,2}};
402 Offset SPREAD_DEAD{FSecs{2,100}};
403 const uint INVOKE_LAG = _raw(
Time{250,0}) /STP;
404 const uint CLEAN_UP = _raw(
Time{100,0}) /STP;
405 const uint INSTANCES = RUN /STP;
406 const uint MAX_TIME = INSTANCES
407 +INVOKE_LAG+2*CLEAN_UP;
409 using TestData = vector<pair<TimeVar, size_t>>;
410 using Subjects = vector<reference_wrapper<Activity>>;
413 TestData testData{INSTANCES};
414 for (
size_t i=0; i<INSTANCES; ++i)
416 const size_t SPREAD = 2*_raw(SPREAD_DEAD);
417 const size_t MIN_DEAD = _raw(BASE_DEADLINE) - _raw(SPREAD_DEAD);
419 auto&[t,r] = testData[i];
425 Subjects subject{INSTANCES, std::ref(dummy)};
427 auto runTest = [&](
auto allocate,
auto invoke) ->
size_t 436 for (
size_t i=0; i<MAX_TIME; ++i)
440 auto const& data = testData[i];
441 subject[i] = allocate(data.first, data.second);
443 if (INVOKE_LAG <= i and i-INVOKE_LAG < INSTANCES)
444 checksum += invoke(subject[i-INVOKE_LAG]);
449 auto benchmark = [INSTANCES](
auto invokeTest)
451 return lib::test::benchmarkTime(invokeTest, INSTANCES);
458 vector<Activity> storage{INSTANCES};
460 auto allocate = [i=0, &storage](
Time,
size_t check)
mutable ->
Activity&
462 return *
new(&storage[i++])
Activity{check,
size_t{55}};
464 auto invoke = [](
Activity& feedActivity)
466 return feedActivity.data_.feed.one;
469 sum1 = runTest (allocate, invoke);
475 auto heapAlloc = [&]{
476 auto allocate = [](
Time,
size_t check)
mutable ->
Activity&
478 return *
new Activity{check,
size_t{55}};
480 auto invoke = [](
Activity& feedActivity)
482 size_t check = feedActivity.data_.feed.one;
483 delete &feedActivity;
487 sum2 = runTest (allocate, invoke);
493 vector<std::shared_ptr<Activity>> manager{INSTANCES};
494 auto sharedAlloc = [&]{
495 auto allocate = [&, i=0](
Time,
size_t check)
mutable ->
Activity&
502 auto invoke = [&, i=0](
Activity& feedActivity)
mutable 504 size_t check = feedActivity.data_.feed.one;
509 sum3 = runTest (allocate, invoke);
518 auto blockFlowAlloc = [&]{
519 auto allocHandle = blockFlow.
until(
Time{BASE_DEADLINE});
520 auto allocate = [&, j=0](
Time t,
size_t check)
mutable ->
Activity&
524 allocHandle = blockFlow.
until(t);
527 return allocHandle.create (check,
size_t{55});
529 auto invoke = [&, i=0](
Activity& feedActivity)
mutable 531 size_t check = feedActivity.data_.feed.one;
532 if (i % CLEAN_UP == 0)
538 sum4 = runTest (allocate, invoke);
542 auto time_noAlloc = benchmark(noAlloc);
545 auto time_heapAlloc = benchmark(heapAlloc);
548 auto time_sharedAlloc = benchmark(sharedAlloc);
550 cout<<
"\n\n■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■□■"<<endl;
553 auto time_blockFlow = benchmark(blockFlowAlloc);
555 Duration expectStep{FSecs{blockFlow.framesPerEpoch(), FPS} * 9/10};
557 cout<<
"\n___Microbenchmark____" 558 <<
"\nnoAlloc : "<<time_noAlloc
559 <<
"\nheapAlloc : "<<time_heapAlloc
560 <<
"\nsharedAlloc : "<<time_sharedAlloc
561 <<
"\nblockFlow : "<<time_blockFlow
562 <<
"\n_____________________\n" 563 <<
"\ninstances.... "<<INSTANCES
564 <<
"\nfps.......... "<<FPS
565 <<
"\nActivities/s. "<<TICK_P_S
566 <<
"\nEpoch(expect) "<<expectStep
567 <<
"\nEpoch (real) "<<blockFlow.getEpochStep()
568 <<
"\ncnt Epochs... "<<watch(blockFlow).cntEpochs()
569 <<
"\nalloc pool... "<<watch(blockFlow).poolSize()
574 CHECK (sum1 == sum2);
575 CHECK (sum1 == sum3);
576 CHECK (sum1 == sum4);
579 CHECK (expectStep - blockFlow.getEpochStep() <
Time(10,0));
583 CHECK (watch(blockFlow).cntEpochs() < 8);
587 CHECK (time_blockFlow < 800);
Allocation scheme for the Scheduler, based on Epoch(s).
a mutable time value, behaving like a plain number, allowing copy and re-accessing ...
signal start of some processing and transition grooming mode ⟼ *work mode
#define ASSERT_VALID_SIGNATURE(_FUN_, _SIG_)
Macro for a compile-time check to verify the given generic functors or lambdas expose some expected s...
Record to describe an Activity, to happen within the Scheduler's control flow.
Memory management scheme for activities and parameter data passed through the Scheduler within the Lu...
Policy template to mix into the BlockFlow allocator, providing the parametrisation for self-regulatio...
Framerate specified as frames per second.
Allocation Extent holding scheduler Activities to be performed altogether before a common deadline...
static const gavl_time_t SCALE
Number of micro ticks (µs) per second as basic time scale.
int rani(uint bound=_iBOUND())
Functions to perform (multithreaded) timing measurement on a given functor.
Local handle to allow allocating a collection of Activities, all sharing a common deadline...
void markEpochOverflow()
Notify and adjust Epoch capacity as consequence of exhausting an Epoch.
Lumiera's internal time value datatype.
Abstract Base Class for all testcases.
Metaprogramming tools for transforming functor types.
void announceAdditionalFlow(FrameRate additionalFps)
provide a hint to the self-regulating allocation scheme.
Simplistic test class runner.
void seedRand()
draw a new random seed from a common nucleus, and re-seed the default-Gen.
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
Activity & create(ARGS &&...args)
Main API operation: allocate a new Activity record.
A collection of frequently used helper functions to support unit testing.
probe window + count-down; activate next Activity, else re-schedule
internal engine »heart beat« for internal maintenance hook(s)
void markEpochUnderflow(TimeVar actualLen, double fillFactor)
On clean-up of past Epochs, the actual fill factor is checked to guess an Epoch duration to make opti...
static const Time NEVER
border condition marker value. NEVER >= any time value
string showType()
diagnostic type output, including const and similar adornments
Offset measures a distance in time.
auto setup(FUN &&workFun)
Helper: setup a Worker-Pool configuration for the test.
Duration is the internal Lumiera time metric.
void discardBefore(Time deadline)
Clean-up all storage related to activities before the given deadline.
lib::time::Time randTime()
create a random but not insane Time value between 1s ...
double boostFactorOverflow() const
< reduced logarithmically, since overflow is detected on individual allocations
a family of time value like entities and their relationships.
basic constant internal time value.
Vault-Layer implementation namespace root.
AllocatorHandle until(Time deadline)
initiate allocations for activities to happen until some deadline
bool isSameObject(A const &a, B const &b)
compare plain object identity, based directly on the referee's memory identities. ...