42 using std::this_thread::sleep_for;
88 CHECK (scheduler.empty());
91 CHECK (1 == task.remainingInvocations());
97 scheduler.defineSchedule(job)
101 CHECK (not scheduler.empty());
104 CHECK (1 == task.remainingInvocations());
107 CHECK (0 == task.remainingInvocations());
137 CHECK (scheduler.empty());
143 CHECK (not scheduler.empty());
146 CHECK (not scheduler.empty());
148 scheduler.terminateProcessing();
149 CHECK (scheduler.empty());
154 CHECK (not scheduler.empty());
182 CHECK (scheduler.empty());
192 for (
uint i=0; i<cnt; ++i)
197 auto LOAD_PEAK_DURATION_us = 2000;
198 auto fatPackage = LOAD_PEAK_DURATION_us/TYPICAL_TIME_FOR_ONE_SCHEDULE_us;
204 cout <<
"Timing : start-up required.."<<offset()<<
" µs"<<endl;
215 _Fmt row{
"%6d | Load: %5.3f Head:%5d Lag:%6d\n"};
217 while (not scheduler.empty())
220 double load = scheduler.getLoadIndicator();
231 peak1_max = max (load, peak1_max);
235 peak1_dur = offset() - peak1_s;
246 peak2_max = max (load, peak2_max);
250 peak2_dur = offset() - peak2_s;
254 cout << row % offset() % load
255 % offset(scheduler.layer1_.headTime())
256 % scheduler.loadControl_.averageLag();
258 uint done = offset();
261 _Fmt peak{
"\nPeak %d ....... %5d +%dµs %34tmax=%3.1f"};
262 cout <<
"-------+-------------+----------+----------"
264 << peak % 1 % peak1_s % peak1_dur % peak1_max
265 << peak % 2 % peak2_s % peak2_dur % peak2_max
266 <<
"\nTick ....... "<<done
270 CHECK (peak1_s > 5000);
271 CHECK (peak1_s < 10000);
272 CHECK (peak2_s > 15000);
273 CHECK (peak2_s < 20000);
274 CHECK (peak1_max > 2.0);
275 CHECK (peak2_max > 2.0);
277 CHECK (done > 50000);
280 cout <<
"\nwaiting for shutdown of WorkForce";
281 while (scheduler.workForce_.size() > 0)
284 cout <<
"." << std::flush;
286 uint shutdown = offset();
287 cout <<
"\nShutdown after "<<shutdown / 1.0e6<<
"sec"<<endl;
288 CHECK (shutdown > 2.0e6);
336 auto post = [&](
Time start)
338 scheduler.layer2_.acquireGoomingToken();
342 auto pullWork = [&] {
344 slip_us = _raw(detector.
invokeTime(probe)) - _raw(start);
345 cout <<
"res:"<<res<<
" delay="<<delay_us<<
"µs slip="<<slip_us<<
"µs"<<endl;
353 auto wasInvoked = [&](
Time start)
356 return invoked >= start
357 and wasClose (invoked, start);
361 cout <<
"pullWork() on empty queue..."<<endl;
366 cout <<
"Due at pullWork()..."<<endl;
368 start = now + t100us;
370 CHECK (not scheduler.empty());
371 CHECK (not wasInvoked(start));
375 CHECK (wasInvoked(start));
376 CHECK (slip_us < 300);
377 CHECK (scheduler.empty());
378 CHECK (delay_us < 20200);
382 CHECK (delay_us < 40);
385 cout <<
"next some time ahead => up-front delay"<<endl;
387 start = now + t500us;
389 CHECK (not scheduler.empty());
393 CHECK (not wasInvoked(start));
394 CHECK (delay_us > 500);
395 CHECK (delay_us < 1000);
397 CHECK (wasInvoked(start));
398 CHECK (scheduler.empty());
399 CHECK (delay_us < 20200);
400 CHECK (slip_us < 300);
404 CHECK (delay_us < 40);
407 cout <<
"follow-up with some distance => follow-up delay"<<endl;
409 start = now + t100us;
414 CHECK (wasInvoked(start));
415 CHECK (slip_us < 300);
416 CHECK (delay_us > 900);
418 CHECK (not scheduler.empty());
422 CHECK (wasInvoked(start));
423 CHECK (slip_us < 400);
424 CHECK (delay_us < 20200);
426 CHECK (scheduler.empty());
431 cout <<
"already tended-next => re-target capacity"<<endl;
433 start = now + t500us;
437 CHECK (start == scheduler.layer1_.headTime());
438 CHECK (not scheduler.loadControl_.tendedNext(start));
440 scheduler.loadControl_.tendNext(start);
441 CHECK ( scheduler.loadControl_.tendedNext(start));
443 CHECK (start == scheduler.layer1_.headTime());
444 CHECK (not scheduler.empty());
447 CHECK (not wasInvoked(start));
448 CHECK (not scheduler.empty());
449 CHECK (delay_us < 6000);
483 CHECK (scheduler.empty());
486 scheduler.defineSchedule(testJob)
491 CHECK (not scheduler.empty());
494 scheduler.layer2_.maybeFeed(scheduler.layer1_);
497 auto entry = scheduler.layer1_.peekHead();
504 CHECK (entry.startTime() - now < _uTicks( 400us));
505 CHECK (entry.deathTime() - now < _uTicks(2400us));
506 CHECK (entry.manifestation == 0);
507 CHECK (entry.isCompulsory ==
false);
546 cout <<
_Fmt{
"Test-Load: Nodes: %d Levels: %d ∅Node/Level: %3.1f Forks: %d Joins: %d"}
556 size_t expectedHash = testLoad.getHash();
560 auto LOAD_BASE = 500us;
561 testLoad.performGraphSynchronously(LOAD_BASE);
562 CHECK (testLoad.getHash() == expectedHash);
564 double referenceTime = testLoad.calcRuntimeReference(LOAD_BASE);
565 cout <<
"refTime(singleThr): "<<referenceTime/1000<<
"ms"<<endl;
573 double performanceTime =
574 testLoad.setupSchedule(scheduler)
575 .withLoadTimeBase(LOAD_BASE)
576 .withJobDeadline(30ms)
579 cout <<
"runTime(Scheduler): "<<performanceTime/1000<<
"ms"<<endl;
582 CHECK (testLoad.getHash() == expectedHash);
585 CHECK (performanceTime < 2*referenceTime);
Diagnostic setup to instrument and observe Activity activations.
Duration is the internal Lumiera time metric.
Offset measures a distance in time.
basic constant internal time value.
a mutable time value, behaving like a plain number, allowing copy and re-accessing
Lumiera's internal time value datatype.
static const Time ANYTIME
border condition marker value. ANYTIME <= 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.
A front-end for using printf-style formatting.
Record to describe an Activity, to happen within the Scheduler's control flow.
@ POST
post a message providing a chain of further time-bound Activities
@ FEED
supply additional payload data for a preceding Activity
@ GATE
probe window + count-down; activate next Activity, else re-schedule
@ INVOKE
dispatch a JobFunctor into a worker thread
@ WORKSTART
signal start of some processing and transition grooming mode ⟼ *work mode
Collector and aggregator for performance data.
Individual frame rendering task, forwarding to a closure.
activity::Proc postChain(ActivationEvent event, SchedulerInvocation &layer1)
This is the primary entrance point to the Scheduler.
»Scheduler-Service« : coordinate render activities.
SchedulerInvocation layer1_
SchedulerCommutator layer2_
Diagnostic context to record and evaluate activations within the Scheduler.
ActivityMatch verifySeqIncrement(uint seqNr)
Activity & buildActivationProbe(string id)
build a rigged HOOK-Activity to record each invocation
Time invokeTime(Activity const *hook)
Job buildMockJob(string id="", Time nominal=lib::test::randTime(), size_t extra=rani())
uint incrementSeq()
increment the internal invocation sequence number
ActivityMatch & arg(ARGS const &...args)
qualifier: additionally match the function arguments
ActivityMatch & beforeInvocation(string match)
void invokeWorkFunction()
static void postNewTask(Scheduler &scheduler, Activity &chain, Time start)
A Generator for synthetic Render Jobs for Scheduler load testing.
TestChainLoad && configureShape_chain_loadBursts()
preconfigured topology: single graph with massive »load bursts«
TestChainLoad && buildTopology()
Use current configuration and seed to (re)build Node connectivity.
Statistic computeGraphStatistics()
Operator on TestChainLoad to evaluate current graph connectivity.
opaque ID attached to each individual job invocation.
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.
Test runner and basic definitions for tests.
Proc
Result instruction from Activity activation.
@ PASS
pass on the activation down the chain
@ WAIT
nothing to do; wait and re-check for work later
const uint TYPICAL_TIME_FOR_ONE_SCHEDULE_us
const StatKey STAT_FORK
forking node
SpecialJobFun onetimeCrunch(milliseconds runTime)
a »throw-away« render-job
const StatKey STAT_NODE
all nodes
const StatKey STAT_JOIN
joining node
FlowDiagnostic< CONF > watch(BlockFlow< CONF > &theFlow)
Vault-Layer implementation namespace root.
Simplistic test class runner.
#define LAUNCHER(_TEST_CLASS_, _GROUPS_)
Service for coordination and dispatch of render activities.
static size_t COMPUTATION_CAPACITY
Nominal »full size« of a pool of concurrent workers.
Generate synthetic computation load for Scheduler performance tests.
#define MARK_TEST_FUN
Macro to mark the current test function in STDOUT.
a family of time value like entities and their relationships.
Test helper to perform temporary manipulations within a test scope.
#define TRANSIENTLY(_OO_)
Macro to simplify capturing assignments.
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...