2024-01-02 21:46:44 +01:00
|
|
|
|
/*
|
|
|
|
|
|
STRESS-TEST-RIG.hpp - setup for stress and performance investigation
|
|
|
|
|
|
|
|
|
|
|
|
Copyright (C) Lumiera.org
|
|
|
|
|
|
2024, Hermann Vosseler <Ichthyostega@web.de>
|
|
|
|
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
|
|
|
|
modify it under the terms of the GNU General Public License as
|
|
|
|
|
|
published by the Free Software Foundation; either version 2 of
|
|
|
|
|
|
the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/** @file stress-test-rig.hpp
|
|
|
|
|
|
** A test bench to conduct performance measurement series. Outfitted especially
|
|
|
|
|
|
** to determine runtime behaviour of the Scheduler and associated parts of the
|
|
|
|
|
|
** Lumiera Engine through systematic execution of load scenarios.
|
|
|
|
|
|
**
|
|
|
|
|
|
** # Scheduler Stress Testing
|
|
|
|
|
|
**
|
|
|
|
|
|
** The point of departure for any stress testing is to show that the subject will
|
|
|
|
|
|
** break in controlled ways only. For the Scheduler this can easily be achieved by
|
|
|
|
|
|
** overloading until job deadlines are broken. Much more challenging however is the
|
|
|
|
|
|
** task to find out about the boundary of regular scheduler operation. This realm
|
|
|
|
|
|
** can be defined by the ability of the scheduler to follow and conform to the
|
|
|
|
|
|
** timings set out explicitly in the schedule. Obviously, short and localised
|
|
|
|
|
|
** load peaks can be accommodated, yet once a persistent backlog builds up,
|
|
|
|
|
|
** the schedule starts to slip and the calculation process will flounder.
|
|
|
|
|
|
**
|
|
|
|
|
|
** A method to determine such a _»breaking point«_ in a systematic way is based on
|
|
|
|
|
|
** building a [synthetic calculation load](\ref test-chain-load.hpp) and establish
|
|
|
|
|
|
** the timings of a test schedule based on a simplified model of expected computation
|
|
|
|
|
|
** expense. By scaling and condensing these schedule timings, a loss of control can
|
|
|
|
|
|
** be provoked, and determined by statistical observation: since the process of
|
|
|
|
|
|
** scheduling contains an essentially random component, persistent overload will be
|
|
|
|
|
|
** indicated by an increasing variance of the overall runtime, and a departure from
|
|
|
|
|
|
** the nominal runtime of the executed schedule.
|
|
|
|
|
|
**
|
|
|
|
|
|
** ## Setup
|
|
|
|
|
|
** To perform this test scheme, an operational Scheduler is required, and an instance
|
|
|
|
|
|
** of the TestChainLoad must be provided, configured with desired load properties.
|
|
|
|
|
|
** The _stressFactor_ of the corresponding generated schedule will be the active parameter
|
2024-01-03 21:02:23 +01:00
|
|
|
|
** of this test, performing a binary search for the _breaking point._ The Measurement
|
|
|
|
|
|
** attempts to narrow down to the point of massive failure, when the ability to somehow
|
|
|
|
|
|
** cope with the schedule completely break down. Based on watching the Scheduler in
|
|
|
|
|
|
** operation, the detection was linked to three conditions, which typically will
|
|
|
|
|
|
** be triggered together, and within a narrow and reproducible parameter range:
|
|
|
|
|
|
** - an individual run counts as _accidentally failed_ when the execution slips
|
|
|
|
|
|
** away by more than 2ms with respect to the defined overall schedule. When more
|
|
|
|
|
|
** than 55% of all observed runs are considered as failed, the first condition is met
|
|
|
|
|
|
** - moreover, the observed ''standard derivation'' must also surpass the same limit
|
|
|
|
|
|
** of > 2ms, which indicates that the Scheduling mechanism is under substantial
|
|
|
|
|
|
** strain; in regular operation, the slip is rather ~ 200µs.
|
|
|
|
|
|
** - the third condition is that the ''averaged delta'' has surpassed 4ms,
|
|
|
|
|
|
** which is 2 times the basic failure indicator.
|
2024-01-02 21:46:44 +01:00
|
|
|
|
**
|
|
|
|
|
|
** ## Observation tools
|
2024-02-23 03:04:24 +01:00
|
|
|
|
** As a complement to the bench::BreakingPoint tool, another tool is provided to
|
|
|
|
|
|
** run a specific Scheduler setup while varying a single control parameter within
|
|
|
|
|
|
** defined limits. This produces a set of (x,y) data, which can be used to search
|
|
|
|
|
|
** for correlations or build a linear regression model to describe the Scheduler's
|
|
|
|
|
|
** behaviour as function of the control parameter. The typical use case would be
|
|
|
|
|
|
** to use the input length (number of Jobs) as control parameter, leading to a
|
|
|
|
|
|
** model for the Scheduler's expense.
|
2024-01-02 21:46:44 +01:00
|
|
|
|
**
|
|
|
|
|
|
** @see TestChainLoad_test
|
|
|
|
|
|
** @see SchedulerStress_test
|
2024-01-04 02:03:05 +01:00
|
|
|
|
** @see binary-search.hpp
|
2024-01-02 21:46:44 +01:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef VAULT_GEAR_TEST_STRESS_TEST_RIG_H
|
|
|
|
|
|
#define VAULT_GEAR_TEST_STRESS_TEST_RIG_H
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "vault/common.hpp"
|
2024-01-04 02:03:05 +01:00
|
|
|
|
#include "lib/binary-search.hpp"
|
2024-01-02 21:46:44 +01:00
|
|
|
|
//#include "test-chain-load.hpp"
|
|
|
|
|
|
//#include "lib/test/transiently.hpp"
|
|
|
|
|
|
|
|
|
|
|
|
#include "vault/gear/scheduler.hpp"
|
|
|
|
|
|
#include "lib/time/timevalue.hpp"
|
|
|
|
|
|
//#include "lib/iter-explorer.hpp"
|
2024-01-03 22:48:49 +01:00
|
|
|
|
#include "lib/meta/function.hpp"
|
2024-01-02 21:46:44 +01:00
|
|
|
|
#include "lib/format-string.hpp"
|
|
|
|
|
|
#include "lib/format-cout.hpp"//////////////////////////TODO RLY?
|
2024-02-24 04:17:05 +01:00
|
|
|
|
#include "lib/util.hpp"
|
2024-01-02 21:46:44 +01:00
|
|
|
|
|
|
|
|
|
|
//#include <functional>
|
2024-01-02 23:51:47 +01:00
|
|
|
|
#include <utility>
|
2024-01-02 21:46:44 +01:00
|
|
|
|
//#include <memory>
|
|
|
|
|
|
//#include <string>
|
2024-01-04 01:32:11 +01:00
|
|
|
|
#include <vector>
|
2024-01-02 21:46:44 +01:00
|
|
|
|
#include <tuple>
|
2024-01-03 16:27:07 +01:00
|
|
|
|
#include <array>
|
2024-01-02 21:46:44 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace vault{
|
|
|
|
|
|
namespace gear {
|
|
|
|
|
|
namespace test {
|
|
|
|
|
|
|
|
|
|
|
|
using util::_Fmt;
|
2024-01-03 22:48:49 +01:00
|
|
|
|
using util::min;
|
|
|
|
|
|
using util::max;
|
2024-01-02 21:46:44 +01:00
|
|
|
|
// using util::isnil;
|
|
|
|
|
|
// using util::limited;
|
|
|
|
|
|
// using util::unConst;
|
|
|
|
|
|
// using util::toString;
|
|
|
|
|
|
// using util::isLimited;
|
|
|
|
|
|
// using lib::time::Time;
|
|
|
|
|
|
// using lib::time::TimeValue;
|
|
|
|
|
|
// using lib::time::FrameRate;
|
|
|
|
|
|
// using lib::time::Duration;
|
|
|
|
|
|
// using lib::test::Transiently;
|
|
|
|
|
|
// using lib::meta::_FunRet;
|
|
|
|
|
|
|
|
|
|
|
|
// using std::string;
|
|
|
|
|
|
// using std::function;
|
2024-01-03 22:48:49 +01:00
|
|
|
|
using std::make_pair;
|
2024-01-02 21:46:44 +01:00
|
|
|
|
using std::make_tuple;
|
|
|
|
|
|
// using std::forward;
|
|
|
|
|
|
// using std::string;
|
|
|
|
|
|
// using std::swap;
|
2024-01-04 01:32:11 +01:00
|
|
|
|
using std::vector;
|
2024-01-02 23:51:47 +01:00
|
|
|
|
using std::move;
|
2024-01-02 21:46:44 +01:00
|
|
|
|
|
|
|
|
|
|
namespace err = lumiera::error; //////////////////////////TODO RLY?
|
|
|
|
|
|
|
|
|
|
|
|
namespace { // Default definitions ....
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-02-23 03:04:24 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** configurable template framework for running Scheduler Stress tests */
|
|
|
|
|
|
class StressRig
|
|
|
|
|
|
: util::NonCopyable
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
/***********************************************************************//**
|
|
|
|
|
|
* Entrance Point: build a stress test measurement setup using a dedicated
|
|
|
|
|
|
* \a TOOL class, takes the configuration \a CONF as template parameter
|
|
|
|
|
|
* and which is assumed to inherit (indirectly) from StressRig.
|
|
|
|
|
|
* @tparam CONF specialised subclass of StressRig with customisation
|
|
|
|
|
|
* @return a builder to configure and then launch the actual test
|
|
|
|
|
|
*/
|
|
|
|
|
|
template<class CONF>
|
|
|
|
|
|
static auto
|
|
|
|
|
|
with()
|
|
|
|
|
|
{
|
|
|
|
|
|
return Launcher<CONF>{};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ======= default configuration (inherited) ======= */
|
|
|
|
|
|
|
|
|
|
|
|
using usec = std::chrono::microseconds;
|
|
|
|
|
|
|
|
|
|
|
|
usec LOAD_BASE = 500us;
|
|
|
|
|
|
usec BASE_EXPENSE = 0us;
|
|
|
|
|
|
bool SCHED_NOTIFY = true;
|
|
|
|
|
|
bool SCHED_DEPENDS = false;
|
|
|
|
|
|
uint CONCURRENCY = work::Config::getDefaultComputationCapacity();
|
|
|
|
|
|
bool INSTRUMENTATION = true;
|
|
|
|
|
|
double EPSILON = 0.01; ///< error bound to abort binary search
|
|
|
|
|
|
double UPPER_STRESS = 0.6; ///< starting point for the upper limit, likely to fail
|
|
|
|
|
|
double FAIL_LIMIT = 2.0; ///< delta-limit when to count a run as failure
|
|
|
|
|
|
double TRIGGER_FAIL = 0.55; ///< %-fact: criterion-1 failures above this rate
|
|
|
|
|
|
double TRIGGER_SDEV = FAIL_LIMIT; ///< in ms : criterion-2 standard derivation
|
|
|
|
|
|
double TRIGGER_DELTA = 2*FAIL_LIMIT; ///< in ms : criterion-3 average delta above this limit
|
|
|
|
|
|
bool showRuns = false; ///< print a line for each individual run
|
|
|
|
|
|
bool showStep = true; ///< print a line for each binary search step
|
|
|
|
|
|
bool showRes = true; ///< print result data
|
|
|
|
|
|
bool showRef = true; ///< calculate single threaded reference time
|
|
|
|
|
|
|
|
|
|
|
|
static uint constexpr REPETITIONS{20};
|
|
|
|
|
|
|
|
|
|
|
|
BlockFlowAlloc bFlow{};
|
|
|
|
|
|
EngineObserver watch{};
|
|
|
|
|
|
Scheduler scheduler{bFlow, watch};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
|
/** Extension point: build the computation topology for this test */
|
|
|
|
|
|
auto
|
2024-02-24 04:17:05 +01:00
|
|
|
|
testLoad(size_t nodes =64)
|
2024-02-23 03:04:24 +01:00
|
|
|
|
{
|
2024-02-24 04:17:05 +01:00
|
|
|
|
return TestChainLoad<>{nodes};
|
2024-02-23 03:04:24 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** (optional) extension point: base configuration of the test ScheduleCtx */
|
|
|
|
|
|
template<class TL>
|
|
|
|
|
|
auto
|
|
|
|
|
|
testSetup (TL& testLoad)
|
|
|
|
|
|
{
|
|
|
|
|
|
return testLoad.setupSchedule(scheduler)
|
|
|
|
|
|
.withJobDeadline(100ms)
|
|
|
|
|
|
.withUpfrontPlanning();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template<class CONF>
|
|
|
|
|
|
struct Launcher : CONF
|
|
|
|
|
|
{
|
|
|
|
|
|
template<template<class> class TOOL, typename...ARGS>
|
|
|
|
|
|
auto
|
|
|
|
|
|
perform (ARGS&& ...args)
|
|
|
|
|
|
{
|
|
|
|
|
|
return TOOL<CONF>{}.perform (std::forward<ARGS> (args)...);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace bench { ///< Specialised tools to investigate scheduler performance
|
|
|
|
|
|
|
|
|
|
|
|
using std::declval;
|
|
|
|
|
|
|
2024-01-02 21:46:44 +01:00
|
|
|
|
|
2024-02-23 03:04:24 +01:00
|
|
|
|
/**************************************************//**
|
2024-01-02 23:51:47 +01:00
|
|
|
|
* Specific stress test scheme to determine the
|
|
|
|
|
|
* »breaking point« where the Scheduler starts to slip
|
|
|
|
|
|
*/
|
2024-01-02 21:46:44 +01:00
|
|
|
|
template<class CONF>
|
2024-02-23 03:04:24 +01:00
|
|
|
|
class BreakingPoint
|
|
|
|
|
|
: public CONF
|
2024-01-02 21:46:44 +01:00
|
|
|
|
{
|
2024-02-23 03:04:24 +01:00
|
|
|
|
using TestLoad = decltype(declval<BreakingPoint>().testLoad());
|
|
|
|
|
|
using TestSetup = decltype(declval<BreakingPoint>().testSetup (declval<TestLoad&>()));
|
2024-01-02 23:51:47 +01:00
|
|
|
|
|
|
|
|
|
|
struct Res
|
|
|
|
|
|
{
|
|
|
|
|
|
double stressFac{0};
|
|
|
|
|
|
double percentOff{0};
|
|
|
|
|
|
double stdDev{0};
|
|
|
|
|
|
double avgDelta{0};
|
|
|
|
|
|
double avgTime{0};
|
2024-01-03 16:27:07 +01:00
|
|
|
|
double expTime{0};
|
2024-01-02 23:51:47 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
2024-02-19 15:58:05 +01:00
|
|
|
|
double adjustmentFac{1.0};
|
|
|
|
|
|
|
2024-01-02 23:51:47 +01:00
|
|
|
|
/** prepare the ScheduleCtx for a specifically parametrised test series */
|
|
|
|
|
|
void
|
|
|
|
|
|
configureTest (TestSetup& testSetup, double stressFac)
|
|
|
|
|
|
{
|
2024-01-07 23:37:54 +01:00
|
|
|
|
testSetup.withLoadTimeBase(CONF::LOAD_BASE)
|
|
|
|
|
|
.withBaseExpense (CONF::BASE_EXPENSE)
|
2024-01-08 22:58:16 +01:00
|
|
|
|
.withSchedNotify (CONF::SCHED_NOTIFY)
|
2024-01-07 23:37:54 +01:00
|
|
|
|
.withSchedDepends(CONF::SCHED_DEPENDS)
|
2024-02-18 23:02:32 +01:00
|
|
|
|
.withInstrumentation(CONF::INSTRUMENTATION) // side-effect: clear existing statistics
|
2024-02-19 15:58:05 +01:00
|
|
|
|
.withAdaptedSchedule(stressFac, CONF::CONCURRENCY, adjustmentFac);
|
2024-01-02 23:51:47 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** perform a repetition of test runs and compute statistics */
|
|
|
|
|
|
Res
|
2024-01-03 16:27:07 +01:00
|
|
|
|
runProbes (TestSetup& testSetup, double stressFac)
|
2024-01-02 23:51:47 +01:00
|
|
|
|
{
|
2024-01-03 16:27:07 +01:00
|
|
|
|
auto sqr = [](auto n){ return n*n; };
|
|
|
|
|
|
Res res;
|
|
|
|
|
|
auto& [sf,pf,sdev,avgD,avgT,expT] = res;
|
2024-01-03 21:02:23 +01:00
|
|
|
|
sf = stressFac;
|
2024-01-03 16:27:07 +01:00
|
|
|
|
std::array<double, CONF::REPETITIONS> runTime;
|
|
|
|
|
|
for (uint i=0; i<CONF::REPETITIONS; ++i)
|
|
|
|
|
|
{
|
|
|
|
|
|
runTime[i] = testSetup.launch_and_wait() / 1000;
|
|
|
|
|
|
avgT += runTime[i];
|
2024-02-18 18:01:21 +01:00
|
|
|
|
testSetup.adaptEmpirically (stressFac, CONF::CONCURRENCY);
|
2024-02-19 17:36:46 +01:00
|
|
|
|
this->adjustmentFac = 1 / (testSetup.getStressFac() / stressFac);
|
2024-01-03 16:27:07 +01:00
|
|
|
|
}
|
2024-02-18 18:01:21 +01:00
|
|
|
|
expT = testSetup.getExpectedEndTime() / 1000;
|
2024-01-03 16:27:07 +01:00
|
|
|
|
avgT /= CONF::REPETITIONS;
|
2024-01-08 22:58:16 +01:00
|
|
|
|
avgD = (avgT-expT); // can be < 0
|
2024-01-03 16:27:07 +01:00
|
|
|
|
for (uint i=0; i<CONF::REPETITIONS; ++i)
|
|
|
|
|
|
{
|
|
|
|
|
|
sdev += sqr (runTime[i] - avgT);
|
2024-01-08 22:58:16 +01:00
|
|
|
|
double delta = (runTime[i] - expT);
|
2024-01-03 16:27:07 +01:00
|
|
|
|
bool fail = (delta > CONF::FAIL_LIMIT);
|
|
|
|
|
|
if (fail)
|
|
|
|
|
|
++ pf;
|
|
|
|
|
|
showRun(i, delta, runTime[i], runTime[i] > avgT, fail);
|
|
|
|
|
|
}
|
2024-01-03 23:53:44 +01:00
|
|
|
|
pf /= CONF::REPETITIONS;
|
2024-01-03 16:27:07 +01:00
|
|
|
|
sdev = sqrt (sdev/CONF::REPETITIONS);
|
|
|
|
|
|
showStep(res);
|
2024-01-02 23:51:47 +01:00
|
|
|
|
return res;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** criterion to decide if this test series constitutes a slipped schedule */
|
|
|
|
|
|
bool
|
|
|
|
|
|
decideBreakPoint (Res& res)
|
|
|
|
|
|
{
|
2024-01-03 21:02:23 +01:00
|
|
|
|
return res.percentOff > CONF::TRIGGER_FAIL
|
|
|
|
|
|
and res.stdDev > CONF::TRIGGER_SDEV
|
|
|
|
|
|
and res.avgDelta > CONF::TRIGGER_DELTA;
|
2024-01-02 23:51:47 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* invoke a binary search to produce a sequence of test series
|
|
|
|
|
|
* with the goal to narrow down the stressFact where the Schedule slips away.
|
|
|
|
|
|
*/
|
|
|
|
|
|
template<class FUN>
|
|
|
|
|
|
Res
|
2024-01-04 01:32:11 +01:00
|
|
|
|
conductBinarySearch (FUN&& runTestCase, vector<Res> const& results)
|
2024-01-02 23:51:47 +01:00
|
|
|
|
{
|
2024-01-04 02:03:05 +01:00
|
|
|
|
double breakPoint = lib::binarySearch_upper (forward<FUN> (runTestCase)
|
|
|
|
|
|
, 0.0, CONF::UPPER_STRESS
|
|
|
|
|
|
, CONF::EPSILON);
|
2024-01-03 22:48:49 +01:00
|
|
|
|
uint s = results.size();
|
|
|
|
|
|
ENSURE (s >= 2);
|
|
|
|
|
|
Res res;
|
|
|
|
|
|
auto& [sf,pf,sdev,avgD,avgT,expT] = res;
|
|
|
|
|
|
// average data over the last three steps investigated for smoothing
|
|
|
|
|
|
uint points = min (results.size(), 3u);
|
|
|
|
|
|
for (uint i=results.size()-points; i<results.size(); ++i)
|
|
|
|
|
|
{
|
2024-01-04 01:32:11 +01:00
|
|
|
|
Res const& resx = results[i];
|
2024-01-03 22:48:49 +01:00
|
|
|
|
pf += resx.percentOff;
|
|
|
|
|
|
sdev += resx.stdDev;
|
|
|
|
|
|
avgD += resx.avgDelta;
|
|
|
|
|
|
avgT += resx.avgTime;
|
|
|
|
|
|
expT += resx.expTime;
|
|
|
|
|
|
}
|
|
|
|
|
|
pf /= points;
|
|
|
|
|
|
sdev /= points;
|
|
|
|
|
|
avgD /= points;
|
|
|
|
|
|
avgT /= points;
|
|
|
|
|
|
expT /= points;
|
2024-01-04 01:32:11 +01:00
|
|
|
|
sf = breakPoint;
|
2024-01-03 22:48:49 +01:00
|
|
|
|
return res;
|
2024-01-02 23:51:47 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-01-03 21:02:23 +01:00
|
|
|
|
|
2024-01-04 01:32:11 +01:00
|
|
|
|
_Fmt fmtRun_ {"....·%-2d: Δ=%4.1f t=%4.1f %s %s"}; // i % Δ % t % t>avg? % fail?
|
2024-02-19 17:36:46 +01:00
|
|
|
|
_Fmt fmtStep_{ "%4.2f| : ∅Δ=%4.1f±%-4.2f ∅t=%4.1f %s %%%-3.0f -- expect:%4.1fms"};// stress % ∅Δ % σ % ∅t % fail % pecentOff % t-expect
|
2024-01-03 16:27:07 +01:00
|
|
|
|
_Fmt fmtResSDv_{"%9s= %5.2f ±%4.2f%s"};
|
2024-01-03 21:02:23 +01:00
|
|
|
|
_Fmt fmtResVal_{"%9s: %5.2f%s"};
|
2024-01-03 16:27:07 +01:00
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
showRun(uint i, double delta, double t, bool over, bool fail)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (CONF::showRuns)
|
|
|
|
|
|
cout << fmtRun_ % i % delta % t % (over? "+":"-") % (fail? "●":"○")
|
|
|
|
|
|
<< endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
showStep(Res& res)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (CONF::showStep)
|
2024-01-04 01:32:11 +01:00
|
|
|
|
cout << fmtStep_ % res.stressFac % res.avgDelta % res.stdDev % res.avgTime
|
|
|
|
|
|
% (decideBreakPoint(res)? "—◆—":"—◇—")
|
2024-02-19 17:36:46 +01:00
|
|
|
|
% (100*res.percentOff) % res.expTime
|
2024-01-03 16:27:07 +01:00
|
|
|
|
<< endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
showRes(Res& res)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (CONF::showRes)
|
|
|
|
|
|
{
|
|
|
|
|
|
cout << fmtResVal_ % "stresFac" % res.stressFac % "" <<endl;
|
|
|
|
|
|
cout << fmtResVal_ % "fail" %(res.percentOff * 100) % '%' <<endl;
|
2024-01-03 23:53:44 +01:00
|
|
|
|
cout << fmtResSDv_ % "delta" % res.avgDelta % res.stdDev % "ms"<<endl;
|
2024-01-03 16:27:07 +01:00
|
|
|
|
cout << fmtResVal_ % "runTime" % res.avgTime % "ms"<<endl;
|
|
|
|
|
|
cout << fmtResVal_ % "expected" % res.expTime % "ms"<<endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2024-01-03 23:53:44 +01:00
|
|
|
|
showRef(TestLoad& testLoad)
|
2024-01-03 16:27:07 +01:00
|
|
|
|
{
|
|
|
|
|
|
if (CONF::showRef)
|
|
|
|
|
|
cout << fmtResVal_ % "refTime"
|
|
|
|
|
|
% (testLoad.calcRuntimeReference(CONF::LOAD_BASE) /1000)
|
|
|
|
|
|
% "ms" << endl;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-01-02 23:51:47 +01:00
|
|
|
|
|
2024-01-02 21:46:44 +01:00
|
|
|
|
public:
|
2024-01-02 23:51:47 +01:00
|
|
|
|
/**
|
|
|
|
|
|
* Launch a measurement sequence to determine the »breaking point«
|
|
|
|
|
|
* for the configured test load and parametrisation of the Scheduler.
|
|
|
|
|
|
* @return a tuple `[stress-factor, ∅delta, ∅run-time]`
|
|
|
|
|
|
*/
|
2024-01-02 21:46:44 +01:00
|
|
|
|
auto
|
2024-02-23 03:04:24 +01:00
|
|
|
|
perform()
|
2024-01-02 21:46:44 +01:00
|
|
|
|
{
|
2024-01-02 23:51:47 +01:00
|
|
|
|
TRANSIENTLY(work::Config::COMPUTATION_CAPACITY) = CONF::CONCURRENCY;
|
|
|
|
|
|
|
|
|
|
|
|
TestLoad testLoad = CONF::testLoad().buildTopology();
|
|
|
|
|
|
TestSetup testSetup = CONF::testSetup (testLoad);
|
|
|
|
|
|
|
2024-01-04 01:32:11 +01:00
|
|
|
|
vector<Res> observations;
|
2024-01-02 23:51:47 +01:00
|
|
|
|
auto performEvaluation = [&](double stressFac)
|
|
|
|
|
|
{
|
|
|
|
|
|
configureTest (testSetup, stressFac);
|
2024-01-03 16:27:07 +01:00
|
|
|
|
auto res = runProbes (testSetup, stressFac);
|
2024-01-04 01:32:11 +01:00
|
|
|
|
observations.push_back (res);
|
|
|
|
|
|
return decideBreakPoint(res);
|
2024-01-02 23:51:47 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
2024-01-04 01:32:11 +01:00
|
|
|
|
Res res = conductBinarySearch (move(performEvaluation), observations);
|
2024-01-03 23:53:44 +01:00
|
|
|
|
showRes (res);
|
|
|
|
|
|
showRef (testLoad);
|
2024-01-02 23:51:47 +01:00
|
|
|
|
return make_tuple (res.stressFac, res.avgDelta, res.avgTime);
|
2024-01-02 21:46:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
};
|
2024-02-23 03:04:24 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**************************************************//**
|
|
|
|
|
|
* Specific test scheme to perform a Scheduler setup
|
|
|
|
|
|
* over a given control parameter range to determine
|
|
|
|
|
|
* correlations
|
|
|
|
|
|
*/
|
|
|
|
|
|
template<class CONF>
|
|
|
|
|
|
class ParameterRange
|
|
|
|
|
|
: public CONF
|
|
|
|
|
|
{
|
2024-02-24 04:17:05 +01:00
|
|
|
|
using TestLoad = decltype(declval<ParameterRange>().testLoad(1));
|
2024-02-23 03:04:24 +01:00
|
|
|
|
using TestSetup = decltype(declval<ParameterRange>().testSetup (declval<TestLoad&>()));
|
|
|
|
|
|
|
2024-02-24 04:17:05 +01:00
|
|
|
|
template<typename PAR>
|
|
|
|
|
|
using Point = std::pair<PAR, double>;
|
2024-02-23 03:04:24 +01:00
|
|
|
|
|
|
|
|
|
|
|
2024-02-24 04:17:05 +01:00
|
|
|
|
template<typename PAR>
|
|
|
|
|
|
void
|
|
|
|
|
|
runTest (Point<PAR>& point)
|
|
|
|
|
|
{
|
|
|
|
|
|
PAR param = point.first;
|
|
|
|
|
|
double stressFac = 1.0;
|
|
|
|
|
|
TestLoad testLoad = CONF::testLoad(param).buildTopology();
|
|
|
|
|
|
TestSetup testSetup = CONF::testSetup (testLoad)
|
|
|
|
|
|
.withLoadTimeBase(CONF::LOAD_BASE)
|
|
|
|
|
|
.withBaseExpense (CONF::BASE_EXPENSE)
|
|
|
|
|
|
.withSchedNotify (CONF::SCHED_NOTIFY)
|
|
|
|
|
|
.withSchedDepends(CONF::SCHED_DEPENDS)
|
2024-03-07 23:29:36 +01:00
|
|
|
|
.withAdaptedSchedule(stressFac, CONF::CONCURRENCY)
|
|
|
|
|
|
.withInstrumentation();
|
|
|
|
|
|
double testMillis = testSetup.launch_and_wait() / 1000;
|
|
|
|
|
|
auto stat = testSetup.getInvocationStatistic();
|
|
|
|
|
|
point.second = stat.coveredTime / 1000;
|
|
|
|
|
|
cout << "x="<<point.first<<"\t y="<<point.second<<"\t e2e="<<testMillis<<"\t conc:"<<stat.avgConcurrency<<" ∅t="<<stat.activeTime/stat.activationCnt<<" ("<<stat.activationCnt<<")"<<endl;
|
2024-02-24 04:17:05 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-02-23 03:04:24 +01:00
|
|
|
|
public:
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Launch a measurement sequence running the Scheduler with a
|
|
|
|
|
|
* varying parameter value to investigate (x,y) correlations.
|
|
|
|
|
|
* @return ////TODO a tuple `[stress-factor, ∅delta, ∅run-time]`
|
|
|
|
|
|
*/
|
2024-02-24 04:17:05 +01:00
|
|
|
|
template<typename PAR>
|
2024-02-23 03:04:24 +01:00
|
|
|
|
auto
|
2024-02-24 04:17:05 +01:00
|
|
|
|
perform (PAR lower, PAR upper)
|
2024-02-23 03:04:24 +01:00
|
|
|
|
{
|
|
|
|
|
|
TRANSIENTLY(work::Config::COMPUTATION_CAPACITY) = CONF::CONCURRENCY;
|
|
|
|
|
|
|
2024-02-24 04:17:05 +01:00
|
|
|
|
PAR dist = upper - lower;
|
|
|
|
|
|
uint cnt = CONF::REPETITIONS;
|
|
|
|
|
|
vector<Point<PAR>> results(cnt);
|
|
|
|
|
|
PAR minP{upper}, maxP{lower};
|
|
|
|
|
|
for (uint i=0; i<cnt; ++i)
|
|
|
|
|
|
{
|
|
|
|
|
|
auto random = double(rand())/RAND_MAX;
|
|
|
|
|
|
PAR pos = lower + PAR(floor (random*dist + 0.5));
|
|
|
|
|
|
results[i].first = pos;
|
|
|
|
|
|
minP = min (pos, minP);
|
|
|
|
|
|
maxP = max (pos, maxP);
|
|
|
|
|
|
}
|
|
|
|
|
|
// ensure the bounds participate in test
|
|
|
|
|
|
if (maxP < upper) results[cnt-2].first = upper;
|
|
|
|
|
|
if (minP > lower) results[cnt-1].first = lower;
|
|
|
|
|
|
|
|
|
|
|
|
for (auto& point : results)
|
|
|
|
|
|
runTest (point);
|
2024-02-23 03:04:24 +01:00
|
|
|
|
|
2024-02-24 04:17:05 +01:00
|
|
|
|
return results;
|
2024-02-23 03:04:24 +01:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
//
|
|
|
|
|
|
}// namespace bench
|
|
|
|
|
|
}}}// namespace vault::gear::test
|
2024-01-02 21:46:44 +01:00
|
|
|
|
#endif /*VAULT_GEAR_TEST_STRESS_TEST_RIG_H*/
|