131 #ifndef VAULT_GEAR_TEST_STRESS_TEST_RIG_H 132 #define VAULT_GEAR_TEST_STRESS_TEST_RIG_H 145 #include "lib/stat/statistic.hpp" 161 using std::make_tuple;
173 template<
size_t maxFan =DEFAULT_FAN>
200 bool INSTRUMENTATION =
true;
212 static uint constexpr REPETITIONS{20};
234 .withLevelDuration(200us)
235 .withJobDeadline(500ms)
236 .withUpfrontPlanning();
242 template<
template<
class>
class TOOL,
typename...ARGS>
244 perform (ARGS&& ...args)
246 return TOOL<CONF>{}.perform (std::forward<ARGS> (args)...);
271 using TestLoad =
typename CONF::TestLoad;
272 using TestSetup =
typename TestLoad::ScheduleCtx;
277 double percentOff{0};
288 testSetup.withInstrumentation(CONF::INSTRUMENTATION)
289 .withAdaptedSchedule(stressFac, CONF::CONCURRENCY, adjustmentFac);
296 auto sqr = [](
auto n){
return n*n; };
298 auto& [sf,pf,sdev,avgD,avgT,expT] = res;
300 std::array<double, CONF::REPETITIONS> runTime;
301 for (uint i=0; i<CONF::REPETITIONS; ++i)
303 runTime[i] = testSetup.launch_and_wait() / 1000;
305 maybeAdaptScaleEmpirically (testSetup, stressFac);
307 expT = testSetup.getExpectedEndTime() / 1000;
308 avgT /= CONF::REPETITIONS;
310 for (uint i=0; i<CONF::REPETITIONS; ++i)
312 sdev += sqr (runTime[i] - avgT);
313 double delta = (runTime[i] - expT);
314 bool fail = (delta > CONF::FAIL_LIMIT);
317 showRun(i, delta, runTime[i], runTime[i] > avgT, fail);
319 pf /= CONF::REPETITIONS;
320 sdev = sqrt (sdev/CONF::REPETITIONS);
329 return res.percentOff > 0.99
330 or( res.percentOff > CONF::TRIGGER_FAIL
331 and res.stdDev > CONF::TRIGGER_SDEV
332 and res.avgDelta > CONF::TRIGGER_DELTA);
344 , 0.0, CONF::UPPER_STRESS
346 uint s = results.size();
349 auto& [sf,pf,sdev,avgD,avgT,expT] = res;
351 uint points = min (results.size(), 3u);
352 for (uint i=results.size()-points; i<results.size(); ++i)
354 Res const& resx = results[i];
355 pf += resx.percentOff;
357 avgD += resx.avgDelta;
358 avgT += resx.avgTime;
359 expT += resx.expTime;
371 double adjustmentFac{1.0};
372 size_t gaugeProbes = 3 * CONF::REPETITIONS;
387 if (not gaugeProbes)
return;
388 double gain = util::limited (0, pow(stressFac, 9), 1);
389 if (gain < 0.2)
return;
391 double formFac = testSetup.determineEmpiricFormFactor (CONF::CONCURRENCY);
392 adjustmentFac = gain*formFac + (1-gain)*adjustmentFac;
393 testSetup.withAdaptedSchedule(stressFac, CONF::CONCURRENCY, adjustmentFac);
398 _Fmt fmtRun_ {
"....·%-2d: Δ=%4.1f t=%4.1f %s %s"};
399 _Fmt fmtStep_{
"%4.2f| : ∅Δ=%4.1f±%-4.2f ∅t=%4.1f %s %%%-3.0f -- expect:%4.1fms"};
400 _Fmt fmtResSDv_{
"%9s= %5.2f ±%4.2f%s"};
401 _Fmt fmtResVal_{
"%9s: %5.2f%s"};
404 showRun(uint i,
double delta,
double t,
bool over,
bool fail)
407 cout << fmtRun_ % i % delta % t % (over?
"+":
"-") % (fail?
"●":
"○")
415 cout << fmtStep_ % res.stressFac % res.avgDelta % res.stdDev % res.avgTime
416 % (decideBreakPoint(res)?
"—◆—":
"—◇—")
417 % (100*res.percentOff) % res.expTime
426 cout << fmtResVal_ %
"stresFac" % res.stressFac %
"" <<endl;
427 cout << fmtResVal_ %
"fail" %(res.percentOff * 100) %
'%' <<endl;
428 cout << fmtResSDv_ %
"delta" % res.avgDelta % res.stdDev %
"ms"<<endl;
429 cout << fmtResVal_ %
"runTime" % res.avgTime %
"ms"<<endl;
430 cout << fmtResVal_ %
"expected" % res.expTime %
"ms"<<endl;
438 cout << fmtResVal_ %
"refTime" 439 % (testSetup.calcRuntimeReference() /1000)
455 TestLoad
testLoad = CONF::testLoad().buildTopology();
456 TestSetup testSetup = CONF::testSetup (testLoad);
458 vector<Res> observations;
459 auto performEvaluation = [&](
double stressFac)
461 configureTest (testSetup, stressFac);
462 auto res = runProbes (testSetup, stressFac);
463 observations.push_back (res);
464 return decideBreakPoint(res);
467 Res res = conductBinarySearch (move(performEvaluation), observations);
470 return make_tuple (res.stressFac, res.avgDelta, res.avgTime);
487 using TestLoad =
typename CONF::TestLoad;
488 using TestSetup =
typename TestLoad::ScheduleCtx;
491 using Param =
typename CONF::Param;
492 using Table =
typename CONF::Table;
496 runTest (Param param, Table& data)
498 TestLoad
testLoad = CONF::testLoad(param).buildTopology();
499 TestSetup
testSetup = CONF::testSetup (testLoad)
500 .withInstrumentation();
501 double millis = testSetup.launch_and_wait() / 1000;
502 auto stat = testSetup.getInvocationStatistic();
503 CONF::collectResult (data, param, millis, stat);
517 Param dist = upper - lower;
518 uint cnt = CONF::REPETITIONS;
519 vector<Param> points;
520 points.reserve (cnt);
521 Param minP{upper}, maxP{lower};
522 for (uint i=0; i<cnt; ++i)
525 Param pos = lower + Param(floor (random*dist + 0.5));
526 points.push_back(pos);
527 minP = min (pos, minP);
528 maxP = max (pos, maxP);
531 if (maxP < upper) points[cnt-2] = upper;
532 if (minP > lower) points[cnt-1] = lower;
535 for (Param point : points)
536 runTest (point, results);
555 template<
typename F,
typename G>
559 lib::stat::RegressionData points;
560 size_t cnt = min (x.data.size(), y.data.size());
561 points.reserve (cnt);
562 for (
size_t i=0; i < cnt; ++i)
563 points.emplace_back (x.data[i], y.data[i]);
564 return lib::stat::computeLinearRegression (points);
578 using Param = size_t;
589 {
return std::tie(param
608 data.jobtime = stat.
activeTime / stat.activationCnt;
609 data.impeded = (stat.timeAtConc(1) + stat.timeAtConc(0))/stat.activationCnt;
614 avgConcurrency (
Table const& results)
620 renderGnuplot (
Table const& results)
623 string csv = results.renderCSV();
624 Param maxParam = * std::max_element (results.param.data.begin(), results.param.data.end());
625 Param xtics = maxParam > 500? 50
631 .
set (KEY_TermSize,
"600,600")
632 .
set (KEY_Xtics, int64_t(xtics))
633 .
set (KEY_Xlabel,
"load size ⟶ number of jobs")
634 .
set (KEY_Ylabel,
"active time ⟶ ms")
635 .
set (KEY_Y2label,
"concurrent threads ⟶")
636 .
set (KEY_Y3label,
"avg job time ⟶ µs")
Res runProbes(TestSetup &testSetup, double stressFac)
perform a repetition of test runs and compute statistics
bool showRuns
print a line for each individual run
double TRIGGER_SDEV
in ms : criterion-2 standard derivation
double TRIGGER_DELTA
in ms : criterion-3 average delta above this limit
#define TRANSIENTLY(_OO_)
Macro to simplify capturing assignments.
Wrapper to simplify notation in tests.
auto testSetup(TestLoad &testLoad)
(optional) extension point: base configuration of the test ScheduleCtx
bool showRef
calculate single threaded reference time
Any copy and copy construction prohibited.
Preconfigured setup for data visualisation with Gnuplot.
auto linearRegression(Column< F > const &x, Column< G > const &y)
Calculate a linear regression model for two table columns.
auto testLoad(size_t nodes=64)
Extension point: build the computation topology for this test.
Configurable template framework for running Scheduler Stress tests Use to build a custom setup class...
Generate synthetic computation load for Scheduler performance tests.
string scatterRegression(ParamRecord params)
Generate a (X,Y)-scatter plot with regression line.
Res conductBinarySearch(FUN &&runTestCase, vector< Res > const &results)
invoke a binary search to produce a sequence of test series with the goal to narrow down the stressFa...
double FAIL_LIMIT
delta-limit when to count a run as failure
A Generator for synthetic Render Jobs for Scheduler load testing.
A front-end for using printf-style formatting.
bool showStep
print a line for each binary search step
double UPPER_STRESS
starting point for the upper limit, likely to fail
void maybeAdaptScaleEmpirically(TestSetup &testSetup, double stressFac)
Attempt to factor out some observable properties, which are considered circumstantial and not a direc...
Read-only view into a segment within a sequence of data.
ScheduleCtx setupSchedule(Scheduler &scheduler)
establish and configure the context used for scheduling computations.
double EPSILON
error bound to abort binary search
double avgConcurrency
amortised concurrency in timespan
»Scheduler-Service« : coordinate render activities.
Service for coordination and dispatch of render activities.
Metaprogramming tools for transforming functor types.
Manage a table with data records, stored persistently as CSV.
Specific test scheme to perform a Scheduler setup over a given control parameter range to determine c...
auto binarySearch_upper(FUN &&fun, PAR lower, PAR upper, PAR epsilon)
entrance point to binary search to ensure the upper point indeed fulfils the test.
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
double coveredTime
overall timespan of observation
Table with data values, stored persistently as CSV file.
Test helper to perform temporary manipulations within a test scope.
Descriptor and Accessor for a data column within a DataTable table.
Mix-in for setup of a #ParameterRange evaluation to watch the processing of a single load peak...
double activeTime
compounded time of thread activity
bool showRes
print result data
Generating (pseudo) random numbers with controlled seed.
Table perform(Param lower, Param upper)
Launch a measurement sequence running the Scheduler with a varying parameter value to investigate (x...
Textbook implementation of the classical binary search over continuous domain.
Setup and wiring for a test run to schedule a computation structure as defined by this TestChainLoad ...
static size_t COMPUTATION_CAPACITY
Nominal »full size« of a pool of concurrent workers.
double TRIGGER_FAIL
%-fact: criterion-1 failures above this rate
double uni()
random double drawn from interval [0.0 ... 1.0[
static auto with()
Entrance Point: build a stress test measurement setup using a dedicated TOOL class, takes the configuration CONF as template parameter and which is assumed to inherit (indirectly) from StressRig.
void configureTest(TestSetup &testSetup, double stressFac)
prepare the ScheduleCtx for a specifically parametrised test series
a family of time value like entities and their relationships.
Random defaultGen
a global default RandomSequencer for mundane purposes
Vault-Layer implementation namespace root.
Collector and aggregator for performance data.
Specific stress test scheme to determine the »breaking point« where the Scheduler starts to slip...
static size_t getDefaultComputationCapacity()
default value for full computing capacity is to use all (virtual) cores.
bool decideBreakPoint(Res &res)
criterion to decide if this test series constitutes a slipped schedule
auto perform()
Launch a measurement sequence to determine the »breaking point« for the configured test load and para...