96 Time deadline = randTime();
100 CHECK (1 ==
watch(bFlow).cntElm());
101 CHECK (1 ==
watch(bFlow).cntEpochs());
102 CHECK (
watch(bFlow).first() > deadline);
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;
140 Epoch& epoch = Epoch::setup (alloc.begin(),
Time{0,10});
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);
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)
259 auto& a4 = allocHandle.
create();
262 CHECK (
watch(bFlow).find(a4) ==
"10s600ms"_expect);
265 for (
uint i=1; i<EXTENT_SIZ; ++i)
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)
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);
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);
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;
347 CHECK (bFlow.
getEpochStep() == movingAverage(step, goal1));
351 CHECK (bFlow.
getEpochStep() == movingAverage(step, goal2));
397 const size_t FPS = 200;
398 const size_t TICK_P_S = FPS * ACTIVITIES_P_FR;
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)
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);
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
568 <<
"\ncnt Epochs... "<<
watch(blockFlow).cntEpochs()
569 <<
"\nalloc pool... "<<
watch(blockFlow).poolSize()
574 CHECK (sum1 == sum2);
575 CHECK (sum1 == sum3);
576 CHECK (sum1 == sum4);
583 CHECK (
watch(blockFlow).cntEpochs() < 8);
587 CHECK (time_blockFlow < 800);
Memory management scheme for activities and parameter data passed through the Scheduler within the Lu...
Duration is the internal Lumiera time metric.
Framerate specified as frames per second.
Offset measures a distance in time.
basic constant internal time value.
static const raw_time_64 SCALE
Number of micro ticks (µs) per second as basic time scale.
a mutable time value, behaving like a plain number, allowing copy and re-accessing
Lumiera's internal time value datatype.
static const Time NEVER
border condition marker value. NEVER >= any time value
Abstract Base Class for all testcases.
void seedRand()
draw a new random seed from a common nucleus, and re-seed the default-Gen.
Record to describe an Activity, to happen within the Scheduler's control flow.
@ GATE
probe window + count-down; activate next Activity, else re-schedule
@ TICK
internal engine »heart beat« for internal maintenance hook(s)
@ WORKSTART
signal start of some processing and transition grooming mode ⟼ *work mode
Local handle to allow allocating a collection of Activities, all sharing a common deadline.
Activity & create(ARGS &&...args)
Main API operation: allocate a new Activity record.
Time currDeadline() const
Allocation scheme for the Scheduler, based on Epoch(s).
blockFlow::Epoch< Allocator > Epoch
void markEpochOverflow()
Notify and adjust Epoch capacity as consequence of exhausting an Epoch.
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...
blockFlow::Strategy< CONF > Strategy
mem::ExtentFamily< Activity, EPOCH_SIZ > Allocator
Duration getEpochStep() const
void discardBefore(Time deadline)
Clean-up all storage related to activities before the given deadline.
AllocatorHandle until(Time deadline)
initiate allocations for activities to happen until some deadline
void announceAdditionalFlow(FrameRate additionalFps)
provide a hint to the self-regulating allocation scheme.
Allocation Extent holding scheduler Activities to be performed altogether before a common deadline.
Metaprogramming tools for detecting and transforming function types.
#define ASSERT_VALID_SIGNATURE(_FUN_, _SIG_)
Macro for a compile-time check to verify the given generic functors or lambdas expose some expected s...
Functions to perform (multithreaded) timing measurement on a given functor.
double benchmarkTime(FUN const &invokeTestCode, const size_t repeatCnt=1)
Helper to invoke a functor or λ to observe its running time.
string showType()
diagnostic type output, including const and similar adornments
lib::time::Time randTime()
create a random but not insane Time value between 1s ... 10min + 500ms
Test runner and basic definitions for tests.
bool isSameObject(A const &a, B const &b)
compare plain object identity, based directly on the referee's memory identities.
const size_t AVERAGE_EPOCHS
const double ACTIVITIES_P_FR
const double BOOST_OVERFLOW
Duration INITIAL_EPOCH_STEP
FlowDiagnostic< CONF > watch(BlockFlow< CONF > &theFlow)
Vault-Layer implementation namespace root.
Simplistic test class runner.
#define LAUNCHER(_TEST_CLASS_, _GROUPS_)
Policy template to mix into the BlockFlow allocator, providing the parametrisation for self-regulatio...
Duration initialEpochStep() const
size_t averageEpochs() const
CONF const & config() const
double boostFactorOverflow() const
< reduced logarithmically, since overflow is detected on individual allocations
size_t initialFrameRate() const
size_t framesPerEpoch() const
A collection of frequently used helper functions to support unit testing.
a family of time value like entities and their relationships.
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...