109 #ifndef VAULT_GEAR_TEST_TEST_CHAIN_LOAD_H 110 #define VAULT_GEAR_TEST_TEST_CHAIN_LOAD_H 131 #include <boost/functional/hash.hpp> 132 #include <functional> 153 using util::isLimited;
154 using util::showHashLSB;
166 using std::make_pair;
171 using std::chrono_literals::operator
""s;
204 _uSec (microseconds ticks)
206 return std::chrono::duration<double, std::micro>{ticks}.count();
230 REQUIRE (0 < concurrency);
231 double speedUp = lw.nodes? lw.nodes / std::ceil (
double(lw.nodes)/concurrency)
233 ENSURE (1.0 <= speedUp);
234 return lw.weight / speedUp;
248 template<
size_t maxFan =DEFAULT_FAN>
258 using _Arr = std::array<Node*, maxFan>;
259 using Iter =
typename _Arr::iterator;
260 using CIter =
typename _Arr::const_iterator;
265 Iter after = _Arr::begin();
267 Iter end() {
return after; }
268 CIter end()
const{
return after; }
269 friend Iter end (
Tab & tab){
return tab.end(); }
270 friend CIter end (
Tab const& tab){
return tab.end(); }
272 Node* front() {
return empty()? nullptr : _Arr::front(); }
273 Node* back() {
return empty()? nullptr : *(after-1); }
275 void clear() { after = _Arr::begin(); }
277 size_t size()
const {
return unConst(
this)->end()-_Arr::begin(); }
278 bool empty()
const {
return 0 == size(); }
283 if (after != _Arr::end())
288 NOTREACHED (
"excess node linkage");
294 size_t level{0}, weight{0};
295 Tab pred{0}, succ{0};
311 addPred (
Node* other)
315 other->succ.add (
this);
320 addSucc (
Node* other)
324 other->pred.add (
this);
327 Node& addPred(
Node& other) {
return addPred(&other); }
328 Node& addSucc(
Node& other) {
return addSucc(&other); }
334 boost::hash_combine (hash,
entry->hash);
338 friend bool isStart (
Node const& n) {
return isnil (n.pred); };
339 friend bool isExit (
Node const& n) {
return isnil (n.succ); };
340 friend bool isInner (
Node const& n) {
return not (isStart(n) or isExit(n)); }
341 friend bool isFork (
Node const& n) {
return 1 < n.succ.size(); }
342 friend bool isJoin (
Node const& n) {
return 1 < n.pred.size(); }
343 friend bool isLink (
Node const& n) {
return 1 == n.pred.size() and 1 == n.succ.size(); }
344 friend bool isKnot (
Node const& n) {
return isFork(n) and isJoin(n); }
347 friend bool isStart (
Node const* n) {
return n and isStart(*n); };
348 friend bool isExit (
Node const* n) {
return n and isExit (*n); };
349 friend bool isInner (
Node const* n) {
return n and isInner(*n); };
350 friend bool isFork (
Node const* n) {
return n and isFork (*n); };
351 friend bool isJoin (
Node const* n) {
return n and isJoin (*n); };
352 friend bool isLink (
Node const* n) {
return n and isLink (*n); };
353 friend bool isKnot (
Node const* n) {
return n and isKnot (*n); };
370 std::unique_ptr<Node[]> nodes_;
373 Rule seedingRule_ {};
374 Rule expansionRule_{};
375 Rule reductionRule_{};
376 Rule pruningRule_ {};
379 Node* frontNode() {
return &nodes_[0]; }
380 Node* afterNode() {
return &nodes_[numNodes_]; }
381 Node* backNode() {
return &nodes_[numNodes_-1];}
386 : nodes_{
new Node[nodeCnt]}
389 REQUIRE (1 < nodeCnt);
393 size_t size()
const {
return numNodes_; }
394 size_t topLevel()
const {
return unConst(
this)->backNode()->level; }
395 size_t getSeed()
const {
return unConst(
this)->frontNode()->hash; }
406 return allNodes().asPtr();
412 return allNodes().filter([](
Node& n){
return isExit(n); });
415 allExitHashes()
const 417 return unConst(
this)->allExitNodes().transform([](
Node& n){
return n.hash; });
424 auto combineBoostHashes = [](
size_t h,
size_t hx){ boost::hash_combine(h,hx);
return h;};
425 return allExitHashes()
426 .filter([](
size_t h){
return h != 0; })
427 .
reduce(lib::iter_explorer::IDENTITY
435 size_t nodeID(
Node const& n){
return nodeID (&n); };
444 seedingRule_ = move(r);
449 expansionRule (
Rule r)
451 expansionRule_ = move(r);
456 reductionRule (
Rule r)
458 reductionRule_ = move(r);
465 pruningRule_ = move(r);
472 weightRule_ = move(r);
479 static Rule value(
size_t v) {
return Rule().fixedVal(v); }
482 rule_atStart (uint v)
486 return isStart(n)?
Rule().fixedVal(v)
496 return isJoin(n) ?
Rule().fixedVal(v)
507 return not (isJoin(n) or isStart(n))
514 rule_atJoin_else (
double p1,
double p2, uint v=1)
516 return Rule().mapping([p1,p2,v](
Node* n)
518 return isJoin(n) ?
Rule().probability(p1).maxVal(v)
519 :
Rule().probability(p2).maxVal(v);
528 pruningRule(value(1));
529 weightRule(value(1));
537 pruningRule(rule().probability(0.8));
538 weightRule(value(1));
546 pruningRule(rule().probability(0.6));
547 seedingRule(rule_atStart(1));
548 weightRule(value(1));
556 seedingRule(rule().probability(0.8).maxVal(1));
557 reductionRule(rule().probability(0.75).maxVal(3));
558 pruningRule(rule_atJoin(1));
559 weightRule(value(1));
568 expansionRule(rule().probability(0.27).maxVal(4));
569 reductionRule(rule().probability(0.44).maxVal(6).minVal(2));
570 weightRule (rule().probability(0.66).maxVal(3));
589 Node* node = frontNode();
600 auto moreNext = [&]{
return next->size() < maxFan; };
601 auto moreNodes = [&]{
return node <= backNode(); };
602 auto spaceLeft = [&]{
return moreNext() and moreNodes(); };
603 auto addNode = [&](
size_t seed =0)
605 Node* n = *next->add (node++);
611 auto apply = [&](
Rule& rule,
Node* n)
615 auto calcNode = [&](
Node* n)
618 n->weight = apply(weightRule_,n);
628 REQUIRE (spaceLeft());
629 for (
Node* o : *curr)
632 if (apply (pruningRule_,o))
634 size_t toSeed = apply (seedingRule_, o);
635 size_t toExpand = apply (expansionRule_,o);
636 while (0 < toSeed and spaceLeft())
638 addNode(this->getSeed());
641 while (0 < toExpand and spaceLeft())
649 r = spaceLeft()? addNode():
nullptr;
650 toReduce = apply (reductionRule_, o);
658 ENSURE (not next->empty());
660 o->addSucc (next->back());
663 ENSURE (not isnil(next) or spaceLeft());
665 addNode(this->getSeed());
666 ENSURE (not next->empty());
669 ENSURE (node > backNode());
671 for (
Node* o : *next)
685 frontNode()->hash = seed;
697 for (
Node& n : allNodes())
698 n.weight = fixedNodeWeight;
709 size_t seed = this->getSeed();
710 for (
Node& n : allNodes())
712 n.hash = isStart(n)? seed : 0;
725 size_t seed = this->getSeed();
726 for (
Node& n : allNodes())
727 n.hash = isStart(n)? seed : 0;
736 generateTopologyDOT()
740 Section nodes(
"Nodes");
741 Section layers(
"Layers");
742 Section topology(
"Topology");
745 Code BOTTOM{
"shape=doublecircle"};
746 Code SEED {
"shape=circle"};
747 Code TOP {
"shape=box, style=rounded"};
752 auto timeLevel = scope(level).rank(
"min ");
754 for (
Node& n : allNodes())
756 size_t i = nodeID(n);
757 string tag{toString(i)+
": "+showHashLSB(n.hash)};
758 if (n.weight) tag +=
"."+toString(n.weight);
759 nodes += node(i).label(tag)
764 for (
Node* suc : n.succ)
765 topology += connect (i, nodeID(*suc));
767 if (level != n.level)
771 ENSURE (level == n.level);
772 timeLevel = scope(level).rank(
"same");
774 timeLevel.add (node(i));
779 return digraph (nodes, layers, topology);
785 cout <<
"───═══───═══───═══───═══───═══───═══───═══───═══───═══───═══───\n" 786 << generateTopologyDOT()
787 <<
"───═══───═══───═══───═══───═══───═══───═══───═══───═══───═══───" 806 return microBenchmark ([&]{ performGraphSynchronously(timeBase,sizeBase); }
813 ,
size_t sizeBase =0);
821 cout <<
_Fmt{
"runtime ∅(%d) = %6.2fms (single-threaded)\n"}
823 % (1e-3 * calcRuntimeReference(timeBase,sizeBase,repeatCnt))
824 <<
"───═══───═══───═══───═══───═══───═══───═══───═══───═══───═══───" 835 .transform([](
Node& n){
return n.weight; })
844 .groupedBy([](
Node& n){
return n.level; }
848 lw.weight += n.weight;
849 lw.endidx = nodeID(n);
859 return allLevelWeights()
860 .transform([schedule=0.0, concurrency]
872 return allLevelWeights()
898 template<
size_t maxFan>
900 :
public std::function<Param(Node*)>
907 return node? node->hash:0;
913 return node? node->level:0;
917 guessHeight (
size_t level)
919 double expectedHeight = 2*maxFan;
920 return level / expectedHeight;
929 static_assert (not
sizeof(SIG),
"Unable to adapt given functor.");
933 template<
typename RES>
936 template<
typename FUN>
940 return [functor=std::forward<FUN>(fun)]
941 (
Node* node) -> _FunRet<FUN>
943 return functor (defaultSrc (node));
951 template<
typename RES>
955 template<
typename FUN>
959 return [functor=std::forward<FUN>(fun)]
960 (
Node* node) -> _FunRet<FUN>
962 return functor (defaultSrc (node)
963 ,guessHeight(level(node)));
969 template<
typename RES>
973 template<
typename FUN>
977 return [functor=std::forward<FUN>(fun)]
978 (
Node* node) -> _FunRet<FUN>
980 return functor (guessHeight(level(node)));
992 : std::pair<size_t,string>
994 using std::pair<size_t,string>::pair;
995 operator size_t const&()
const {
return this->first; }
996 operator string const&()
const {
return this->second;}
1009 const uint CAT = KEYS.size();
1010 const uint IDX_SEED = 1;
1015 prepareEvaluations()
1017 return std::array<std::function<uint(NOD&)>, CAT>
1018 { [](NOD& ){
return 1; }
1019 , [](NOD& n){
return isStart(n);}
1020 , [](NOD& n){
return isExit(n); }
1021 , [](NOD& n){
return isInner(n);}
1022 , [](NOD& n){
return isFork(n); }
1023 , [](NOD& n){
return isJoin(n); }
1024 , [](NOD& n){
return isLink(n); }
1025 , [](NOD& n){
return isKnot(n); }
1026 , [](NOD& n){
return n.weight; }
1031 using VecU = std::vector<uint>;
1032 using LevelSums = std::array<uint, CAT>;
1053 addPoint (uint levelID, uint sublevelID, uint width, uint items)
1055 REQUIRE (levelID == data.size());
1056 REQUIRE (width > 0);
1057 data.push_back (items);
1061 pLW += items / double(width);
1062 cL += levelID * items;
1063 cLW += levelID * items/double(width);
1064 sL += sublevelID * items;
1065 sLW += sublevelID * items/double(width);
1069 closeAverages (uint nodes, uint levels, uint segments,
double avgheight)
1071 REQUIRE (levels == data.size());
1072 REQUIRE (levels > 0);
1073 frac = cnt / double(nodes);
1075 cLW = pLW? cLW/pLW :0;
1077 sLW = pLW? sLW/pLW :0;
1083 ASSERT (avgheight >= 1.0);
1084 if (avgheight > 1.0)
1103 double avgheight{0};
1107 std::array<Indicator, CAT> indicators;
1118 addPoint (uint levelWidth, uint sublevelID, LevelSums& particulars)
1121 nodes += levelWidth;
1122 width.push_back (levelWidth);
1123 sublevel.push_back (sublevelID);
1124 ASSERT (levels == width.size());
1125 ASSERT (0 < levels);
1126 ASSERT (0 < levelWidth);
1127 for (uint i=0; i< CAT; ++i)
1128 indicators[i].addPoint (levels-1, sublevelID, levelWidth, particulars[i]);
1132 closeAverages (uint segs, uint maxSublevelID)
1135 maxheight = maxSublevelID + 1;
1136 avgheight = levels / double(segments);
1137 for (uint i=0; i< CAT; ++i)
1138 indicators[i].closeAverages (nodes,levels,segments,avgheight);
1145 width.reserve (lvls);
1146 sublevel.reserve(lvls);
1147 for (uint i=0; i< CAT; ++i)
1150 indicators[i].data.reserve(lvls);
1169 template<
size_t maxFan>
1173 auto totalLevels = uint(topLevel());
1174 auto classify = prepareEvaluations<Node>();
1176 LevelSums particulars{0};
1182 auto detectSubgraphs = [&]{
1183 if (width==1 and particulars[IDX_SEED]==1)
1189 maxsublevel = max (sublevel,maxsublevel);
1192 for (
Node& node : allNodes())
1194 if (level != node.level)
1198 stat.addPoint (width, sublevel, particulars);
1202 ENSURE (level == node.level);
1203 particulars = LevelSums{0};
1208 for (uint i=0; i<CAT; ++i)
1209 particulars[i] += classify[i](node);
1211 ENSURE (level == topLevel());
1213 stat.addPoint (width, sublevel, particulars);
1214 stat.closeAverages (segs, maxsublevel);
1252 template<
size_t maxFan>
1256 cout <<
"INDI: cnt frac ∅pS ∅pL ∅pLW γL◆ γLW◆ γL⬙ γLW⬙\n";
1257 _Fmt line{
"%4s: %3d %3.0f%% %5.1f %5.2f %4.2f %4.2f %4.2f %4.2f %4.2f\n"};
1258 Statistic stat = computeGraphStatistics();
1259 for (uint i=0; i< CAT; ++i)
1262 cout << line % KEYS[i]
1274 cout <<
_Fmt{
"LEVL: %3d\n"} % stat.levels;
1275 cout <<
_Fmt{
"SEGS: %3d h = ∅%3.1f / max.%2d\n"}
1279 cout <<
"───═══───═══───═══───═══───═══───═══───═══───═══───═══───═══───" 1313 using Sink =
volatile size_t;
1320 return mem? memSpeed : cpuSpeed;
1326 bool useAllocation =
false;
1332 if (scaleStep == 0 or timeBase < 1us)
1334 return useAllocation? benchmarkTime ([
this,scaleStep]{ causeMemProcessLoad (scaleStep); })
1335 : benchmarkTime ([
this,scaleStep]{ causeComputationLoad(scaleStep); });
1342 return microBenchmark ([&]{ invoke(scaleStep);}
1351 performIncrementalCalibration();
1352 useAllocation =
true;
1353 performIncrementalCalibration();
1359 if (not isCalibrated())
1364 isCalibrated()
const 1371 roundsNeeded (uint scaleStep)
1373 auto desiredMicros = scaleStep*timeBase.count();
1374 return uint64_t(desiredMicros*computationSpeed(useAllocation));
1378 allocNeeded (uint scaleStep)
1380 auto cnt = roundsNeeded(scaleStep);
1381 auto siz = max (scaleStep * sizeBase, 1u);
1382 auto rep = max (cnt/siz, 1u);
1385 return make_pair (siz,rep);
1389 causeComputationLoad (uint scaleStep)
1391 auto round = roundsNeeded (scaleStep);
1394 for ( ; 0 < round; --round)
1395 boost::hash_combine (scree,scree);
1401 causeMemProcessLoad (uint scaleStep)
1403 auto [siz,round] = allocNeeded (scaleStep);
1406 *memBlock.front() = sink+1;
1407 for ( ; 0 < round; --round)
1408 for (
size_t i=0; i<memBlock.size()-1; ++i)
1409 memBlock[i+1] += memBlock[i];
1410 sink = *memBlock.back();
1417 uint step4gauge = 1;
1418 double micros = benchmark (step4gauge);
1419 auto stepsDone = roundsNeeded (step4gauge);
1420 return stepsDone / micros;
1424 performIncrementalCalibration()
1426 double& speed = computationSpeed(useAllocation);
1427 double prev{speed},delta;
1429 speed = determineSpeed();
1430 delta = abs(1.0 - speed/prev);
1433 while (delta > 0.05);
1445 [runTime](JobParameter) ->
void 1448 crunch.timeBase = runTime;
1462 template<
size_t maxFan>
1467 compuLoad.timeBase = timeBase;
1471 compuLoad.useAllocation =
false;
1475 compuLoad.sizeBase = sizeBase;
1476 compuLoad.useAllocation =
true;
1478 compuLoad.maybeCalibrate();
1480 size_t seed = this->getSeed();
1481 for (
Node& n : allNodes())
1483 n.hash = isStart(n)? seed : 0;
1485 compuLoad.
invoke (n.weight);
1513 string diagnostic()
const =0;
1514 void invokeJobOperation (JobParameter) =0;
1523 buildInstanceID (
HashVal)
const override 1532 HashVal res = hashr (invoKey.frameNumber);
1546 invoKey.code.w1 = idx;
1553 return size_t(invoKey.code.w1);
1557 encodeLevel (
size_t level)
1559 return Time{testGrid().timeOf (FrameCnt(level))};
1565 return testGrid().gridPoint (nominalTime);
1578 template<
size_t maxFan>
1591 : startNode_{&startNode}
1601 if (watch_) watch_->markEnter();
1602 size_t nodeIdx = decodeNodeID (param.invoKey);
1603 size_t level = decodeLevel (
TimeValue{param.nominalTime});
1604 Node& target = startNode_[nodeIdx];
1605 ASSERT (target.level == level);
1607 if (compuLoad_ and target.weight)
1608 compuLoad_->
invoke (target.weight);
1610 if (watch_) watch_->markLeave();
1613 string diagnostic()
const override 1615 return _Fmt{
"ChainCalc(w:%d)◀%s"}
1617 % util::showAdr(startNode_);
1626 template<
size_t maxFan>
1632 function<void(size_t,size_t)> scheduleCalcJob_;
1633 function<void(Node*,Node*)> markDependency_;
1634 function<void(size_t,size_t,size_t,bool)> continuation_;
1642 template<
class CAL,
class DEP,
class CON>
1644 CAL&& schedule, DEP&& markDepend,
1646 : scheduleCalcJob_{forward<CAL> (schedule)}
1647 , markDependency_{forward<DEP> (markDepend)}
1648 , continuation_{forward<CON> (continuation)}
1650 , nodes_{&nodeArray}
1661 size_t start{currIdx_};
1662 size_t reachedLevel{0};
1663 size_t targetNodeIDX = decodeNodeID (param.invoKey);
1664 for ( ; currIdx_<maxCnt_; ++currIdx_)
1666 Node* n = &nodes_[currIdx_];
1667 if (currIdx_ <= targetNodeIDX)
1668 reachedLevel = n->level;
1670 if (n->level > reachedLevel)
1672 scheduleCalcJob_(currIdx_, n->level);
1673 for (Node* pred: n->pred)
1674 markDependency_(pred,n);
1676 ENSURE (currIdx_ > 0);
1677 continuation_(start, currIdx_-1, reachedLevel, currIdx_ < maxCnt_);
1681 string diagnostic()
const override 1702 template<
size_t maxFan>
1714 double stressFac_{1.0};
1717 uint blockLoadFac_{2};
1721 microseconds preRoll_{guessPlanningPreroll()};
1724 std::vector<TimeVar> startTimes_{};
1725 std::promise<void> signalDone_{};
1727 std::unique_ptr<ComputationalLoad> compuLoad_;
1728 std::unique_ptr<RandomChainCalcFunctor<maxFan>> calcFunctor_;
1729 std::unique_ptr<RandomChainPlanFunctor<maxFan>> planFunctor_;
1731 std::unique_ptr<lib::IncidenceCount> watchInvocations_;
1741 .manifestation(manID_)
1742 .startTime (jobStartTime(level, idx))
1743 .lifeWindow (deadline_);
1744 Node& n = chainLoad_.nodes_[idx];
1747 schedule_[idx].post();
1755 size_t predIdx = chainLoad_.nodeID (pred);
1756 size_t succIdx = chainLoad_.nodeID (succ);
1757 bool unlimitedTime = not schedNotify_;
1758 schedule_[predIdx].linkToSuccessor (schedule_[succIdx], unlimitedTime);
1763 continuation (
size_t chunkStart,
size_t lastNodeIDX,
size_t levelDone,
bool work_left)
1767 size_t nextChunkEndNode = calcNextChunkEnd (lastNodeIDX);
1769 ,planningJob (nextChunkEndNode)
1775 .manifestation (manID_)
1779 for (
size_t exitIDX : lastExitNodes (chunkStart))
1780 wakeUp.linkToPredecessor (schedule_[exitIDX]);
1788 auto finished = attachNewCompletionSignal();
1789 size_t numNodes = chainLoad_.size();
1790 size_t firstChunkEndNode = calcNextChunkEnd(0);
1791 schedule_.allocate (numNodes);
1792 compuLoad_->maybeCalibrate();
1795 ,[
this](
size_t i,
size_t l){ disposeStep(i,l); }
1796 ,[
this](
auto* p,
auto* s) { setDependency(p,s);}
1797 ,[
this](
size_t s,
size_t n,
size_t l,
bool w)
1798 { continuation(s,n,l,w); }
1800 startTime_ = anchorSchedule();
1810 : chainLoad_{mother}
1811 , scheduler_{scheduler}
1823 return benchmarkTime ([
this]
1830 ERROR_LOG_AND_RETHROW(
test,
"Scheduler testing")
1835 if (isnil (startTimes_))
1836 fillDefaultSchedule();
1841 return jobTime - startTimes_.front();
1846 getExpectedEndTime()
1848 return _raw(startTimes_.back() - startTimes_.front()
1849 +
Duration{nodeExpense_}*(chainLoad_.size()/stressFac_));
1853 getInvocationStatistic()
1855 return watchInvocations_? watchInvocations_->
evaluate()
1860 calcRuntimeReference()
1862 microseconds timeBase = compuLoad_->timeBase;
1863 size_t sizeBase = compuLoad_->useAllocation? compuLoad_->sizeBase : 0;
1864 return chainLoad_.calcRuntimeReference (timeBase, sizeBase);
1867 double getStressFac() {
return stressFac_; }
1874 withInstrumentation (
bool doWatch =
true)
1880 .expectIncidents(chainLoad_.size());
1883 watchInvocations_.reset();
1888 withPlanningStep (microseconds planningTime_per_node)
1891 preRoll_ = guessPlanningPreroll();
1896 withChunkSize (
size_t nodes_per_chunk)
1898 chunkSize_ = nodes_per_chunk;
1899 preRoll_ = guessPlanningPreroll();
1904 withPreRoll (microseconds planning_headstart)
1906 preRoll_ = planning_headstart;
1911 withUpfrontPlanning()
1913 withChunkSize (chainLoad_.size());
1919 withLevelDuration (microseconds fixedTime_per_level)
1926 withBaseExpense (microseconds fixedTime_per_node)
1928 nodeExpense_ = _uTicks(fixedTime_per_node);
1933 withSchedDepends (
bool explicitly)
1935 schedDepends_ = explicitly;
1940 withSchedNotify (
bool doSetTime =
true)
1942 schedNotify_ = doSetTime;
1955 if (not concurrency)
1956 concurrency = defaultConcurrency();
1957 ENSURE (isLimited (1u, concurrency, 3*defaultConcurrency()));
1958 REQUIRE (formFac > 0.0);
1959 stressFac /= formFac;
1960 withLevelDuration (compuLoad_->timeBase);
1961 fillAdaptedSchedule (stressFac, concurrency);
1966 determineEmpiricFormFactor (uint concurrency=0)
1968 if (not watchInvocations_)
return 1.0;
1969 auto stat = watchInvocations_->evaluate();
1970 if (0 == stat.activationCnt)
return 1.0;
1972 ENSURE (0.0 < stat.avgConcurrency);
1973 if (not concurrency)
1974 concurrency = defaultConcurrency();
1975 double worktimeRatio = 1 - stat.timeAtConc(0) / stat.coveredTime;
1976 double workConcurrency = stat.avgConcurrency / worktimeRatio;
1977 double weightSum = chainLoad_.calcWeightSum();
1978 double expectedCompoundedWeight = chainLoad_.calcExpectedCompoundedWeight(concurrency);
1979 double expectedConcurrency = weightSum / expectedCompoundedWeight;
1980 double formFac = 1 / (workConcurrency / expectedConcurrency);
1981 double expectedNodeTime = _uSec(compuLoad_->timeBase) * weightSum / chainLoad_.size();
1982 double realAvgNodeTime = stat.activeTime / stat.activationCnt;
1983 formFac *= realAvgNodeTime / expectedNodeTime;
1988 withJobDeadline (microseconds deadline_after_start)
1990 deadline_ = deadline_after_start;
1995 withAnnouncedLoadFactor (uint factor_on_levelSpeed)
1997 blockLoadFac_ = factor_on_levelSpeed;
2011 compuLoad_->timeBase = timeBase;
2018 compuLoad_->timeBase = 0us;
2028 compuLoad_->useAllocation =
false;
2032 compuLoad_->sizeBase = sizeBase;
2033 compuLoad_->useAllocation =
true;
2043 std::promise<void> notYetTriggered;
2044 signalDone_.swap (notYetTriggered);
2045 return signalDone_.get_future();
2049 awaitBlocking(std::future<void> signal)
2051 if (std::future_status::timeout == signal.wait_for (
SAFETY_TIMEOUT))
2052 throw err::Fatal(
"Timeout on Scheduler test exceeded.");
2056 calcJob (
size_t idx,
size_t level)
2058 return Job{*calcFunctor_
2059 , calcFunctor_->encodeNodeID(idx)
2060 , calcFunctor_->encodeLevel(level)
2065 planningJob (
size_t endNodeIDX)
2067 return Job{*planFunctor_
2068 , planFunctor_->encodeNodeID(endNodeIDX)
2078 signalDone_.set_value();
2080 return Job{ wakeUpFun
2087 guessPlanningPreroll()
2089 return microseconds(_raw(
Time{chunkSize_ / planSpeed_}));
2095 return FrameRate{levelSpeed_ * blockLoadFac_};
2099 calcNextChunkEnd (
size_t lastNodeIDX)
2101 lastNodeIDX += chunkSize_;
2102 return min (lastNodeIDX, chainLoad_.size()-1);
2108 Time anchor = RealClock::now() + _uTicks(preRoll_);
2109 if (isnil (startTimes_))
2110 fillDefaultSchedule();
2111 size_t numPoints = chainLoad_.topLevel()+2;
2112 ENSURE (startTimes_.size() == numPoints);
2113 Offset base{startTimes_.front(), anchor};
2114 for (
size_t level=0; level<numPoints; ++level)
2115 startTimes_[level] += base;
2120 fillDefaultSchedule()
2122 size_t numPoints = chainLoad_.topLevel()+2;
2124 startTimes_.clear();
2125 startTimes_.reserve (numPoints);
2126 for (
size_t level=0; level<numPoints; ++level)
2127 startTimes_.push_back (level / levelSpeed_);
2131 fillAdaptedSchedule (
double stressFact, uint concurrency)
2133 REQUIRE (stressFact > 0);
2134 stressFac_ = stressFact;
2135 size_t numPoints = chainLoad_.topLevel()+2;
2136 startTimes_.clear();
2137 startTimes_.reserve (numPoints);
2138 startTimes_.push_back (Time::ZERO);
2139 chainLoad_.levelScheduleSequence (concurrency)
2140 .transform([&](
double scheduleFact){
return (scheduleFact/stressFac_) *
Offset{1,levelSpeed_};})
2141 .effuse(startTimes_);
2145 jobStartTime (
size_t level,
size_t nodeIDX =0)
2147 ENSURE (level < startTimes_.size());
2148 return startTimes_[level]
2149 + nodeExpense_ * (nodeIDX/stressFac_);
2153 lastExitNodes (
size_t lastChunkStartIDX)
2155 return chainLoad_.allExitNodes()
2156 .transform([&](
Node& n){
return chainLoad_.nodeID(n); })
2157 .
filter([=](
size_t idx){
return idx >= lastChunkStartIDX; });
2161 calcPlanScheduleTime (
size_t lastNodeIDX)
2169 lastNodeIDX = min (lastNodeIDX, chainLoad_.size()-1);
2170 size_t nextChunkLevel = chainLoad_.nodes_[lastNodeIDX].level;
2171 nextChunkLevel = nextChunkLevel>2? nextChunkLevel-2 : 0;
2172 return jobStartTime(nextChunkLevel) - _uTicks(preRoll_);
2181 template<
size_t maxFan>
static const Time ANYTIME
border condition marker value. ANYTIME <= any time value
Distribution indicators for one kind of evaluation.
a mutable time value, behaving like a plain number, allowing copy and re-accessing ...
const double LOAD_SPEED_BASELINE
initial assumption for calculation speed (without calibration)
const StatKey STAT_NODE
all nodes
void setDependency(Node *pred, Node *succ)
Callback: define a dependency between scheduled jobs.
Table with connections to other Node records.
double calcExpectedCompoundedWeight(uint concurrency=1)
calculate the simplified/theoretic reduction of compounded weight through concurrency ...
double cLW
weight centre level width-reduced
const StatKey STAT_LINK
1:1 linking node
const StatKey STAT_SEED
seed node
TestChainLoad && configureShape_short_chains3_interleaved()
preconfigured topology: simple 3-step chains, starting interleaved
const Duration SCHEDULE_PLAN_STEP
time budget to reserve for each node to be planned and scheduled
const size_t LOAD_DEFAULT_MEM_SIZE
default allocation base size used if ComputationalLoad.useAllocation
const size_t LOAD_BENCHMARK_RUNS
repetition count for calibration benchmark for ComputationalLoad
Types marked with this mix-in may be moved and move-assigned.
void seedCalcStream(Job planningJob, ManifestationID manID=ManifestationID(), FrameRate expectedAdditionalLoad=FrameRate(25))
Set the Scheduler to work on a new CalcStream.
const StatKey STAT_KNOT
knot (joins and forks)
Support for generation of Graphviz-DOT code for structure visualisation.
const bool SCHED_NOTIFY
explicitly set notify dispatch time to the dependency's start time.
#define TRANSIENTLY(_OO_)
Macro to simplify capturing assignments.
static InvocationInstanceID encodeNodeID(size_t idx)
package the node-index to invoke.
Adaptor to handle further mapping functions.
auto explore(IT &&srcSeq)
start building a IterExplorer by suitably wrapping the given iterable source.
const StatKey STAT_WGHT
node weight
bool filter(Placement< DummyMO > const &candidate)
a filter predicate to pick some objects from a resultset.
AnyPair entry(Query< TY > const &query, typename WrapReturn< TY >::Wrapper &obj)
helper to simplify creating mock table entries, wrapped correctly
Framerate specified as frames per second.
const bool SCHED_DEPENDS
explicitly schedule a dependent job (or rely on NOTIFY)
auto levelScheduleSequence(uint concurrency=1)
sequence of the summed compounded weight factors after each level
static Rule rule()
Abbreviation for starting rules.
const StatKey STAT_EXIT
exit node
const StatKey STAT_INNR
inner node
Types marked with this mix-in may be moved but not copied.
const Offset SCHEDULE_WAKE_UP
tiny offset to place the final wake-up job behind any systematic schedule
TestChainLoad && configure_isolated_nodes()
preconfigured topology: only unconnected seed/exit nodes
A configurable one-time job to invoke some special function.
ScheduleCtx && withAdaptedSchedule(double stressFac=1.0, uint concurrency=0, double formFac=1.0)
Establish a differentiated schedule per level, taking node weights into account.
int rani(uint bound=_iBOUND())
Primary class template for std::hash.
ScheduleSpec post()
build Activity chain and hand-over to the Scheduler.
size_t getHash() const
global hash is the combination of all exit node hashes != 0
double invoke(uint scaleStep=1)
cause a delay by computational load
TestChainLoad && buildTopology()
Use current configuration and seed to (re)build Node connectivity.
Functions to perform (multithreaded) timing measurement on a given functor.
void continuation(size_t chunkStart, size_t lastNodeIDX, size_t levelDone, bool work_left)
continue planning: schedule follow-up planning job
Front-end to configure a special job functor for one-time use.
A Result Value confined into fixed bounds.
A Generator for synthetic Render Jobs for Scheduler load testing.
A front-end for using printf-style formatting.
Record and evaluate concurrent activations.
static const Duration NIL
constant to indicate "no duration"
const size_t DEFAULT_CHUNKSIZE
number of computation jobs to prepare in each planning round
void invokeJobOperation(JobParameter param) override
render job invocation to trigger one batch of scheduling; the installed callback-λ should actually pl...
const Duration SCHEDULE_NODE_STEP
additional time step to include in the plan for each job (node).
ScheduleSpec defineSchedule(Job job)
Render Job builder: start definition of a schedule to invoke the given Job.
double pS
average per segment
const auto SAFETY_TIMEOUT
maximum time limit for test run, abort if exceeded
Lumiera's internal time value datatype.
auto microBenchmark(FUN const &testSubject, const size_t repeatCnt=DEFAULT_RUNS)
perform a simple looped microbenchmark.
double benchmark(uint scaleStep=1)
std::future< void > attachNewCompletionSignal()
push away any existing wait state and attach new clean state
double benchmarkTime(FUN const &invokeTestCode, const size_t repeatCnt=1)
Helper to invoke a functor or λ to observe its running time.
ScheduleCtx setupSchedule(Scheduler &scheduler)
establish and configure the context used for scheduling computations.
double sL
weight centre on subgraph
Derived specific exceptions within Lumiera's exception hierarchy.
»Scheduler-Service« : coordinate render activities.
static const FrameRate STEP
1 frame per second
Service for coordination and dispatch of render activities.
Marker for current (and obsolete) manifestations of a CalcStream processed by the Render-Engine...
TestChainLoad && recalculate()
Recalculate all node hashes and propagate seed value.
TestChainLoad && configureShape_short_chains2()
preconfigured topology: isolated simple 2-step chains
const size_t DEFAULT_SIZ
default node count for the complete load graph
void disposeStep(size_t idx, size_t level)
Callback: place a single job into the scheduler.
size_t calcWeightSum()
overall sum of configured node weights
Render JobFunctor to invoke the calculation of a single Node.
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
double computeWeightFactor(LevelWeight const &lw, uint concurrency)
simplified model for expense of a level of nodes, computed concurrently.
double calcRuntimeReference(microseconds timeBase=LOAD_DEFAULT_TIME, size_t sizeBase=0, size_t repeatCnt=GRAPH_BENCHMARK_RUNS)
Conduct a number of benchmark runs over processing the Graph synchronously.
double pL
average per level
Managed uninitialised Heap-allocated storage with array like access.
static size_t defaultSrc(Node *node)
by default use Node-hash directly as source of randomness
Baseclass: JobFunctor to invoke TestChainLoad.
TestChainLoad && configureShape_chain_loadBursts()
preconfigured topology: single graph with massive »load bursts«
test and diagnostic and research
const size_t DEFAULT_FAN
default maximum connectivity per Node
const StatKey STAT_JOIN
joining node
TestChainLoad && setSeed(size_t seed=rani())
Set the overall seed value.
Statistic computeGraphStatistics()
Operator on TestChainLoad to evaluate current graph connectivity.
void continueMetaJob(Time nextStart, Job planningJob, ManifestationID manID=ManifestationID())
Place a follow-up job-planning job into the timeline.
A raw memory block with proper alignment and array access.
Policy/Binding for generation of random parameters by »drawing« based on the node-hash.
Definition of a render job.
Test helper to perform temporary manipulations within a test scope.
A recorder for concurrent incidences.
opaque ID attached to each individual job invocation.
const StatKey STAT_FORK
forking node
Library functions to support the formation of grid-aligned time values.
Basic set of definitions and includes commonly used together (Vault).
size_t HashVal
a STL compatible hash value
Interface of the closure for frame rendering jobs.
Offset measures a distance in time.
Statistic data calculated for a given chain-load topology.
const auto STANDARD_DEADLINE
deadline to use for each individual computation job
double sLW
weight centre on subgraph width-reduced
Duration is the internal Lumiera time metric.
NUM constexpr limited(NB lowerBound, NUM val, NB upperBound)
force a numeric to be within bounds, inclusively
const microseconds LOAD_DEFAULT_TIME
default time delay produced by ComputationalLoad at Node.weight==1
static double & computationSpeed(bool mem)
const double UPFRONT_PLANNING_BOOST
factor to increase the computed pre-roll to ensure up-front planning
Setup and wiring for a test run to schedule a computation structure as defined by this TestChainLoad ...
Accessing a STL element range through a Lumiera forward iterator, An instance of this iterator adapte...
SpecialJobFun onetimeCrunch(milliseconds runTime)
a »throw-away« render-job
Build a component to select limited values randomly.
static size_t COMPUTATION_CAPACITY
Nominal »full size« of a pool of concurrent workers.
TestChainLoad && clearNodeHashes()
Clear node hashes and propagate seed value.
TestChainLoad && configureShape_short_segments3_interleaved()
preconfigured topology: simple interwoven 3-step graph segments
double launch_and_wait()
dispose one complete run of the graph into the scheduler
double cL
weight centre level for this indicator
Statistic evaluate()
Visit all data captured thus far, construct an unified timeline and then compute statistics evaluatio...
Abstraction of a value alignment grid.
Building tree expanding and backtracking evaluations within hierarchical scopes.
Render JobFunctor to perform chunk wise planning of Node jobs to calculate a complete Chain-Load grap...
Token to capture a value and restore original when leaving scope.
void invokeJobOperation(JobParameter param) override
render job invocation to trigger one Node recalculation
double frac
fraction of all nodes
Individual frame rendering task, forwarding to a closure.
a family of time value like entities and their relationships.
double pLW
average per level and level-width
const size_t GRAPH_BENCHMARK_RUNS
repetition count for reference calculation of a complete node graph
basic constant internal time value.
size_t nodeID(Node const *n)
const Duration SCHEDULE_LEVEL_STEP
time budget to plan for the calculation of each »time level« of jobs
typename _Fun< FUN >::Ret _FunRet
abbreviation for referring to a function's return type
Vault-Layer implementation namespace root.
auto allLevelWeights()
calculate node weights aggregated per level
TestChainLoad && performGraphSynchronously(microseconds timeBase=LOAD_DEFAULT_TIME, size_t sizeBase=0)
Emulate complete graph processing in a single threaded loop.
static size_t getDefaultComputationCapacity()
default value for full computing capacity is to use all (virtual) cores.
A calibratable CPU load to be invoked from a node job functor.
TestChainLoad && printTopologyStatistics()
Print a tabular summary of graph characteristics.
Simple stand-alone Quantiser implementation based on a constant sized gird.
TestChainLoad && setWeight(size_t fixedNodeWeight=1)
Set a fixed weight for all nodes.
uint cnt
global sum over all levels