118 #ifndef VAULT_GEAR_TEST_TEST_CHAIN_LOAD_H 119 #define VAULT_GEAR_TEST_TEST_CHAIN_LOAD_H 140 #include <boost/functional/hash.hpp> 141 #include <functional> 162 using util::isLimited;
163 using util::showHashLSB;
175 using std::make_pair;
180 using std::chrono_literals::operator
""s;
213 _uSec (microseconds ticks)
215 return std::chrono::duration<double, std::micro>{ticks}.count();
239 REQUIRE (0 < concurrency);
240 double speedUp = lw.nodes? lw.nodes / std::ceil (
double(lw.nodes)/concurrency)
242 ENSURE (1.0 <= speedUp);
243 return lw.weight / speedUp;
257 template<
size_t maxFan =DEFAULT_FAN>
267 using _Arr = std::array<Node*, maxFan>;
268 using Iter =
typename _Arr::iterator;
269 using CIter =
typename _Arr::const_iterator;
274 Iter after = _Arr::begin();
276 Iter end() {
return after; }
277 CIter end()
const{
return after; }
278 friend Iter end (
Tab & tab){
return tab.end(); }
279 friend CIter end (
Tab const& tab){
return tab.end(); }
281 Node* front() {
return empty()? nullptr : _Arr::front(); }
282 Node* back() {
return empty()? nullptr : *(after-1); }
284 void clear() { after = _Arr::begin(); }
286 size_t size()
const {
return unConst(
this)->end()-_Arr::begin(); }
287 bool empty()
const {
return 0 == size(); }
292 if (after != _Arr::end())
297 NOTREACHED (
"excess node linkage");
303 size_t level{0}, weight{0};
304 Tab pred{0}, succ{0};
320 addPred (
Node* other)
324 other->succ.add (
this);
329 addSucc (
Node* other)
333 other->pred.add (
this);
336 Node& addPred(
Node& other) {
return addPred(&other); }
337 Node& addSucc(
Node& other) {
return addSucc(&other); }
343 boost::hash_combine (hash,
entry->hash);
347 friend bool isStart (
Node const& n) {
return isnil (n.pred); };
348 friend bool isExit (
Node const& n) {
return isnil (n.succ); };
349 friend bool isInner (
Node const& n) {
return not (isStart(n) or isExit(n)); }
350 friend bool isFork (
Node const& n) {
return 1 < n.succ.size(); }
351 friend bool isJoin (
Node const& n) {
return 1 < n.pred.size(); }
352 friend bool isLink (
Node const& n) {
return 1 == n.pred.size() and 1 == n.succ.size(); }
353 friend bool isKnot (
Node const& n) {
return isFork(n) and isJoin(n); }
356 friend bool isStart (
Node const* n) {
return n and isStart(*n); };
357 friend bool isExit (
Node const* n) {
return n and isExit (*n); };
358 friend bool isInner (
Node const* n) {
return n and isInner(*n); };
359 friend bool isFork (
Node const* n) {
return n and isFork (*n); };
360 friend bool isJoin (
Node const* n) {
return n and isJoin (*n); };
361 friend bool isLink (
Node const* n) {
return n and isLink (*n); };
362 friend bool isKnot (
Node const* n) {
return n and isKnot (*n); };
379 std::unique_ptr<Node[]> nodes_;
382 Rule seedingRule_ {};
383 Rule expansionRule_{};
384 Rule reductionRule_{};
385 Rule pruningRule_ {};
388 Node* frontNode() {
return &nodes_[0]; }
389 Node* afterNode() {
return &nodes_[numNodes_]; }
390 Node* backNode() {
return &nodes_[numNodes_-1];}
395 : nodes_{
new Node[nodeCnt]}
398 REQUIRE (1 < nodeCnt);
402 size_t size()
const {
return numNodes_; }
403 size_t topLevel()
const {
return unConst(
this)->backNode()->level; }
404 size_t getSeed()
const {
return unConst(
this)->frontNode()->hash; }
415 return allNodes().asPtr();
421 return allNodes().filter([](
Node& n){
return isExit(n); });
424 allExitHashes()
const 426 return unConst(
this)->allExitNodes().transform([](
Node& n){
return n.hash; });
433 auto combineBoostHashes = [](
size_t h,
size_t hx){ boost::hash_combine(h,hx);
return h;};
434 return allExitHashes()
435 .filter([](
size_t h){
return h != 0; })
436 .
reduce(lib::iter_explorer::IDENTITY
444 size_t nodeID(
Node const& n){
return nodeID (&n); };
453 seedingRule_ = move(r);
458 expansionRule (
Rule r)
460 expansionRule_ = move(r);
465 reductionRule (
Rule r)
467 reductionRule_ = move(r);
474 pruningRule_ = move(r);
481 weightRule_ = move(r);
488 static Rule value(
size_t v) {
return Rule().fixedVal(v); }
491 rule_atStart (uint v)
495 return isStart(n)?
Rule().fixedVal(v)
505 return isJoin(n) ?
Rule().fixedVal(v)
516 return not (isJoin(n) or isStart(n))
523 rule_atJoin_else (
double p1,
double p2, uint v=1)
525 return Rule().mapping([p1,p2,v](
Node* n)
527 return isJoin(n) ?
Rule().probability(p1).maxVal(v)
528 :
Rule().probability(p2).maxVal(v);
537 pruningRule(value(1));
538 weightRule(value(1));
546 pruningRule(rule().probability(0.8));
547 weightRule(value(1));
555 pruningRule(rule().probability(0.6));
556 seedingRule(rule_atStart(1));
557 weightRule(value(1));
565 seedingRule(rule().probability(0.8).maxVal(1));
566 reductionRule(rule().probability(0.75).maxVal(3));
567 pruningRule(rule_atJoin(1));
568 weightRule(value(1));
577 expansionRule(rule().probability(0.27).maxVal(4));
578 reductionRule(rule().probability(0.44).maxVal(6).minVal(2));
579 weightRule (rule().probability(0.66).maxVal(3));
598 Node* node = frontNode();
609 auto moreNext = [&]{
return next->size() < maxFan; };
610 auto moreNodes = [&]{
return node <= backNode(); };
611 auto spaceLeft = [&]{
return moreNext() and moreNodes(); };
612 auto addNode = [&](
size_t seed =0)
614 Node* n = *next->add (node++);
620 auto apply = [&](
Rule& rule,
Node* n)
624 auto calcNode = [&](
Node* n)
627 n->weight = apply(weightRule_,n);
637 REQUIRE (spaceLeft());
638 for (
Node* o : *curr)
641 if (apply (pruningRule_,o))
643 size_t toSeed = apply (seedingRule_, o);
644 size_t toExpand = apply (expansionRule_,o);
645 while (0 < toSeed and spaceLeft())
647 addNode(this->getSeed());
650 while (0 < toExpand and spaceLeft())
658 r = spaceLeft()? addNode():
nullptr;
659 toReduce = apply (reductionRule_, o);
667 ENSURE (not next->empty());
669 o->addSucc (next->back());
672 ENSURE (not isnil(next) or spaceLeft());
674 addNode(this->getSeed());
675 ENSURE (not next->empty());
678 ENSURE (node > backNode());
680 for (
Node* o : *next)
694 frontNode()->hash = seed;
706 for (
Node& n : allNodes())
707 n.weight = fixedNodeWeight;
718 size_t seed = this->getSeed();
719 for (
Node& n : allNodes())
721 n.hash = isStart(n)? seed : 0;
734 size_t seed = this->getSeed();
735 for (
Node& n : allNodes())
736 n.hash = isStart(n)? seed : 0;
745 generateTopologyDOT()
749 Section nodes(
"Nodes");
750 Section layers(
"Layers");
751 Section topology(
"Topology");
754 Code BOTTOM{
"shape=doublecircle"};
755 Code SEED {
"shape=circle"};
756 Code TOP {
"shape=box, style=rounded"};
761 auto timeLevel = scope(level).rank(
"min ");
763 for (
Node& n : allNodes())
765 size_t i = nodeID(n);
766 string tag{toString(i)+
": "+showHashLSB(n.hash)};
767 if (n.weight) tag +=
"."+toString(n.weight);
768 nodes += node(i).label(tag)
773 for (
Node* suc : n.succ)
774 topology += connect (i, nodeID(*suc));
776 if (level != n.level)
780 ENSURE (level == n.level);
781 timeLevel = scope(level).rank(
"same");
783 timeLevel.add (node(i));
788 return digraph (nodes, layers, topology);
794 cout <<
"───═══───═══───═══───═══───═══───═══───═══───═══───═══───═══───\n" 795 << generateTopologyDOT()
796 <<
"───═══───═══───═══───═══───═══───═══───═══───═══───═══───═══───" 815 return microBenchmark ([&]{ performGraphSynchronously(timeBase,sizeBase); }
822 ,
size_t sizeBase =0);
830 cout <<
_Fmt{
"runtime ∅(%d) = %6.2fms (single-threaded)\n"}
832 % (1e-3 * calcRuntimeReference(timeBase,sizeBase,repeatCnt))
833 <<
"───═══───═══───═══───═══───═══───═══───═══───═══───═══───═══───" 844 .transform([](
Node& n){
return n.weight; })
853 .groupedBy([](
Node& n){
return n.level; }
857 lw.weight += n.weight;
858 lw.endidx = nodeID(n);
868 return allLevelWeights()
869 .transform([schedule=0.0, concurrency]
881 return allLevelWeights()
907 template<
size_t maxFan>
909 :
public std::function<Param(Node*)>
916 return node? node->hash:0;
922 return node? node->level:0;
926 guessHeight (
size_t level)
928 double expectedHeight = 2*maxFan;
929 return level / expectedHeight;
938 static_assert (not
sizeof(SIG),
"Unable to adapt given functor.");
942 template<
typename RES>
945 template<
typename FUN>
949 return [functor=std::forward<FUN>(fun)]
950 (
Node* node) -> _FunRet<FUN>
952 return functor (defaultSrc (node));
960 template<
typename RES>
964 template<
typename FUN>
968 return [functor=std::forward<FUN>(fun)]
969 (
Node* node) -> _FunRet<FUN>
971 return functor (defaultSrc (node)
972 ,guessHeight(level(node)));
978 template<
typename RES>
982 template<
typename FUN>
986 return [functor=std::forward<FUN>(fun)]
987 (
Node* node) -> _FunRet<FUN>
989 return functor (guessHeight(level(node)));
1001 : std::pair<size_t,string>
1003 using std::pair<size_t,string>::pair;
1004 operator size_t const&()
const {
return this->first; }
1005 operator string const&()
const {
return this->second;}
1018 const uint CAT = KEYS.size();
1019 const uint IDX_SEED = 1;
1024 prepareEvaluations()
1026 return std::array<std::function<uint(NOD&)>, CAT>
1027 { [](NOD& ){
return 1; }
1028 , [](NOD& n){
return isStart(n);}
1029 , [](NOD& n){
return isExit(n); }
1030 , [](NOD& n){
return isInner(n);}
1031 , [](NOD& n){
return isFork(n); }
1032 , [](NOD& n){
return isJoin(n); }
1033 , [](NOD& n){
return isLink(n); }
1034 , [](NOD& n){
return isKnot(n); }
1035 , [](NOD& n){
return n.weight; }
1040 using VecU = std::vector<uint>;
1041 using LevelSums = std::array<uint, CAT>;
1062 addPoint (uint levelID, uint sublevelID, uint width, uint items)
1064 REQUIRE (levelID == data.size());
1065 REQUIRE (width > 0);
1066 data.push_back (items);
1070 pLW += items / double(width);
1071 cL += levelID * items;
1072 cLW += levelID * items/double(width);
1073 sL += sublevelID * items;
1074 sLW += sublevelID * items/double(width);
1078 closeAverages (uint nodes, uint levels, uint segments,
double avgheight)
1080 REQUIRE (levels == data.size());
1081 REQUIRE (levels > 0);
1082 frac = cnt / double(nodes);
1084 cLW = pLW? cLW/pLW :0;
1086 sLW = pLW? sLW/pLW :0;
1092 ASSERT (avgheight >= 1.0);
1093 if (avgheight > 1.0)
1112 double avgheight{0};
1116 std::array<Indicator, CAT> indicators;
1127 addPoint (uint levelWidth, uint sublevelID, LevelSums& particulars)
1130 nodes += levelWidth;
1131 width.push_back (levelWidth);
1132 sublevel.push_back (sublevelID);
1133 ASSERT (levels == width.size());
1134 ASSERT (0 < levels);
1135 ASSERT (0 < levelWidth);
1136 for (uint i=0; i< CAT; ++i)
1137 indicators[i].addPoint (levels-1, sublevelID, levelWidth, particulars[i]);
1141 closeAverages (uint segs, uint maxSublevelID)
1144 maxheight = maxSublevelID + 1;
1145 avgheight = levels / double(segments);
1146 for (uint i=0; i< CAT; ++i)
1147 indicators[i].closeAverages (nodes,levels,segments,avgheight);
1154 width.reserve (lvls);
1155 sublevel.reserve(lvls);
1156 for (uint i=0; i< CAT; ++i)
1159 indicators[i].data.reserve(lvls);
1178 template<
size_t maxFan>
1182 auto totalLevels = uint(topLevel());
1183 auto classify = prepareEvaluations<Node>();
1185 LevelSums particulars{0};
1191 auto detectSubgraphs = [&]{
1192 if (width==1 and particulars[IDX_SEED]==1)
1198 maxsublevel = max (sublevel,maxsublevel);
1201 for (
Node& node : allNodes())
1203 if (level != node.level)
1207 stat.addPoint (width, sublevel, particulars);
1211 ENSURE (level == node.level);
1212 particulars = LevelSums{0};
1217 for (uint i=0; i<CAT; ++i)
1218 particulars[i] += classify[i](node);
1220 ENSURE (level == topLevel());
1222 stat.addPoint (width, sublevel, particulars);
1223 stat.closeAverages (segs, maxsublevel);
1261 template<
size_t maxFan>
1265 cout <<
"INDI: cnt frac ∅pS ∅pL ∅pLW γL◆ γLW◆ γL⬙ γLW⬙\n";
1266 _Fmt line{
"%4s: %3d %3.0f%% %5.1f %5.2f %4.2f %4.2f %4.2f %4.2f %4.2f\n"};
1267 Statistic stat = computeGraphStatistics();
1268 for (uint i=0; i< CAT; ++i)
1271 cout << line % KEYS[i]
1283 cout <<
_Fmt{
"LEVL: %3d\n"} % stat.levels;
1284 cout <<
_Fmt{
"SEGS: %3d h = ∅%3.1f / max.%2d\n"}
1288 cout <<
"───═══───═══───═══───═══───═══───═══───═══───═══───═══───═══───" 1322 using Sink =
volatile size_t;
1329 return mem? memSpeed : cpuSpeed;
1335 bool useAllocation =
false;
1341 if (scaleStep == 0 or timeBase < 1us)
1343 return useAllocation? benchmarkTime ([
this,scaleStep]{ causeMemProcessLoad (scaleStep); })
1344 : benchmarkTime ([
this,scaleStep]{ causeComputationLoad(scaleStep); });
1351 return microBenchmark ([&]{ invoke(scaleStep);}
1360 performIncrementalCalibration();
1361 useAllocation =
true;
1362 performIncrementalCalibration();
1368 if (not isCalibrated())
1373 isCalibrated()
const 1380 roundsNeeded (uint scaleStep)
1382 auto desiredMicros = scaleStep*timeBase.count();
1383 return uint64_t(desiredMicros*computationSpeed(useAllocation));
1387 allocNeeded (uint scaleStep)
1389 auto cnt = roundsNeeded(scaleStep);
1390 auto siz = max (scaleStep * sizeBase, 1u);
1391 auto rep = max (cnt/siz, 1u);
1394 return make_pair (siz,rep);
1398 causeComputationLoad (uint scaleStep)
1400 auto round = roundsNeeded (scaleStep);
1403 for ( ; 0 < round; --round)
1404 boost::hash_combine (scree,scree);
1410 causeMemProcessLoad (uint scaleStep)
1412 auto [siz,round] = allocNeeded (scaleStep);
1415 *memBlock.front() = sink+1;
1416 for ( ; 0 < round; --round)
1417 for (
size_t i=0; i<memBlock.size()-1; ++i)
1418 memBlock[i+1] += memBlock[i];
1419 sink = *memBlock.back();
1426 uint step4gauge = 1;
1427 double micros = benchmark (step4gauge);
1428 auto stepsDone = roundsNeeded (step4gauge);
1429 return stepsDone / micros;
1433 performIncrementalCalibration()
1435 double& speed = computationSpeed(useAllocation);
1436 double prev{speed},delta;
1438 speed = determineSpeed();
1439 delta = abs(1.0 - speed/prev);
1442 while (delta > 0.05);
1454 [runTime](JobParameter) ->
void 1457 crunch.timeBase = runTime;
1471 template<
size_t maxFan>
1476 compuLoad.timeBase = timeBase;
1480 compuLoad.useAllocation =
false;
1484 compuLoad.sizeBase = sizeBase;
1485 compuLoad.useAllocation =
true;
1487 compuLoad.maybeCalibrate();
1489 size_t seed = this->getSeed();
1490 for (
Node& n : allNodes())
1492 n.hash = isStart(n)? seed : 0;
1494 compuLoad.
invoke (n.weight);
1522 string diagnostic()
const =0;
1523 void invokeJobOperation (JobParameter) =0;
1532 buildInstanceID (
HashVal)
const override 1541 HashVal res = hashr (invoKey.frameNumber);
1555 invoKey.code.w1 = idx;
1562 return size_t(invoKey.code.w1);
1566 encodeLevel (
size_t level)
1568 return Time{testGrid().timeOf (FrameCnt(level))};
1574 return testGrid().gridPoint (nominalTime);
1587 template<
size_t maxFan>
1600 : startNode_{&startNode}
1610 if (watch_) watch_->markEnter();
1611 size_t nodeIdx = decodeNodeID (param.invoKey);
1612 size_t level = decodeLevel (
TimeValue{param.nominalTime});
1613 Node& target = startNode_[nodeIdx];
1614 ASSERT (target.level == level);
1616 if (compuLoad_ and target.weight)
1617 compuLoad_->
invoke (target.weight);
1619 if (watch_) watch_->markLeave();
1622 string diagnostic()
const override 1624 return _Fmt{
"ChainCalc(w:%d)◀%s"}
1626 % util::showAddr(startNode_);
1635 template<
size_t maxFan>
1641 function<void(size_t,size_t)> scheduleCalcJob_;
1642 function<void(Node*,Node*)> markDependency_;
1643 function<void(size_t,size_t,size_t,bool)> continuation_;
1651 template<
class CAL,
class DEP,
class CON>
1653 CAL&& schedule, DEP&& markDepend,
1655 : scheduleCalcJob_{forward<CAL> (schedule)}
1656 , markDependency_{forward<DEP> (markDepend)}
1657 , continuation_{forward<CON> (continuation)}
1659 , nodes_{&nodeArray}
1670 size_t start{currIdx_};
1671 size_t reachedLevel{0};
1672 size_t targetNodeIDX = decodeNodeID (param.invoKey);
1673 for ( ; currIdx_<maxCnt_; ++currIdx_)
1675 Node* n = &nodes_[currIdx_];
1676 if (currIdx_ <= targetNodeIDX)
1677 reachedLevel = n->level;
1679 if (n->level > reachedLevel)
1681 scheduleCalcJob_(currIdx_, n->level);
1682 for (Node* pred: n->pred)
1683 markDependency_(pred,n);
1685 ENSURE (currIdx_ > 0);
1686 continuation_(start, currIdx_-1, reachedLevel, currIdx_ < maxCnt_);
1690 string diagnostic()
const override 1711 template<
size_t maxFan>
1723 double stressFac_{1.0};
1726 uint blockLoadFac_{2};
1730 microseconds preRoll_{guessPlanningPreroll()};
1733 std::vector<TimeVar> startTimes_{};
1734 std::promise<void> signalDone_{};
1736 std::unique_ptr<ComputationalLoad> compuLoad_;
1737 std::unique_ptr<RandomChainCalcFunctor<maxFan>> calcFunctor_;
1738 std::unique_ptr<RandomChainPlanFunctor<maxFan>> planFunctor_;
1740 std::unique_ptr<lib::IncidenceCount> watchInvocations_;
1750 .manifestation(manID_)
1751 .startTime (jobStartTime(level, idx))
1752 .lifeWindow (deadline_);
1753 Node& n = chainLoad_.nodes_[idx];
1756 schedule_[idx].post();
1764 size_t predIdx = chainLoad_.nodeID (pred);
1765 size_t succIdx = chainLoad_.nodeID (succ);
1766 bool unlimitedTime = not schedNotify_;
1767 schedule_[predIdx].linkToSuccessor (schedule_[succIdx], unlimitedTime);
1772 continuation (
size_t chunkStart,
size_t lastNodeIDX,
size_t levelDone,
bool work_left)
1776 size_t nextChunkEndNode = calcNextChunkEnd (lastNodeIDX);
1778 ,planningJob (nextChunkEndNode)
1784 .manifestation (manID_)
1788 for (
size_t exitIDX : lastExitNodes (chunkStart))
1789 wakeUp.linkToPredecessor (schedule_[exitIDX]);
1797 auto finished = attachNewCompletionSignal();
1798 size_t numNodes = chainLoad_.size();
1799 size_t firstChunkEndNode = calcNextChunkEnd(0);
1800 schedule_.allocate (numNodes);
1801 compuLoad_->maybeCalibrate();
1804 ,[
this](
size_t i,
size_t l){ disposeStep(i,l); }
1805 ,[
this](
auto* p,
auto* s) { setDependency(p,s);}
1806 ,[
this](
size_t s,
size_t n,
size_t l,
bool w)
1807 { continuation(s,n,l,w); }
1809 startTime_ = anchorSchedule();
1819 : chainLoad_{mother}
1820 , scheduler_{scheduler}
1832 return benchmarkTime ([
this]
1839 ERROR_LOG_AND_RETHROW(
test,
"Scheduler testing")
1844 if (isnil (startTimes_))
1845 fillDefaultSchedule();
1850 return jobTime - startTimes_.front();
1855 getExpectedEndTime()
1857 return _raw(startTimes_.back() - startTimes_.front()
1858 +
Duration{nodeExpense_}*(chainLoad_.size()/stressFac_));
1862 getInvocationStatistic()
1864 return watchInvocations_? watchInvocations_->
evaluate()
1869 calcRuntimeReference()
1871 microseconds timeBase = compuLoad_->timeBase;
1872 size_t sizeBase = compuLoad_->useAllocation? compuLoad_->sizeBase : 0;
1873 return chainLoad_.calcRuntimeReference (timeBase, sizeBase);
1876 double getStressFac() {
return stressFac_; }
1883 withInstrumentation (
bool doWatch =
true)
1889 .expectIncidents(chainLoad_.size());
1892 watchInvocations_.reset();
1897 withPlanningStep (microseconds planningTime_per_node)
1900 preRoll_ = guessPlanningPreroll();
1905 withChunkSize (
size_t nodes_per_chunk)
1907 chunkSize_ = nodes_per_chunk;
1908 preRoll_ = guessPlanningPreroll();
1913 withPreRoll (microseconds planning_headstart)
1915 preRoll_ = planning_headstart;
1920 withUpfrontPlanning()
1922 withChunkSize (chainLoad_.size());
1928 withLevelDuration (microseconds fixedTime_per_level)
1935 withBaseExpense (microseconds fixedTime_per_node)
1937 nodeExpense_ = _uTicks(fixedTime_per_node);
1942 withSchedDepends (
bool explicitly)
1944 schedDepends_ = explicitly;
1949 withSchedNotify (
bool doSetTime =
true)
1951 schedNotify_ = doSetTime;
1964 if (not concurrency)
1965 concurrency = defaultConcurrency();
1966 ENSURE (isLimited (1u, concurrency, 3*defaultConcurrency()));
1967 REQUIRE (formFac > 0.0);
1968 stressFac /= formFac;
1969 withLevelDuration (compuLoad_->timeBase);
1970 fillAdaptedSchedule (stressFac, concurrency);
1975 determineEmpiricFormFactor (uint concurrency=0)
1977 if (not watchInvocations_)
return 1.0;
1978 auto stat = watchInvocations_->evaluate();
1979 if (0 == stat.activationCnt)
return 1.0;
1981 ENSURE (0.0 < stat.avgConcurrency);
1982 if (not concurrency)
1983 concurrency = defaultConcurrency();
1984 double worktimeRatio = 1 - stat.timeAtConc(0) / stat.coveredTime;
1985 double workConcurrency = stat.avgConcurrency / worktimeRatio;
1986 double weightSum = chainLoad_.calcWeightSum();
1987 double expectedCompoundedWeight = chainLoad_.calcExpectedCompoundedWeight(concurrency);
1988 double expectedConcurrency = weightSum / expectedCompoundedWeight;
1989 double formFac = 1 / (workConcurrency / expectedConcurrency);
1990 double expectedNodeTime = _uSec(compuLoad_->timeBase) * weightSum / chainLoad_.size();
1991 double realAvgNodeTime = stat.activeTime / stat.activationCnt;
1992 formFac *= realAvgNodeTime / expectedNodeTime;
1997 withJobDeadline (microseconds deadline_after_start)
1999 deadline_ = deadline_after_start;
2004 withAnnouncedLoadFactor (uint factor_on_levelSpeed)
2006 blockLoadFac_ = factor_on_levelSpeed;
2020 compuLoad_->timeBase = timeBase;
2027 compuLoad_->timeBase = 0us;
2037 compuLoad_->useAllocation =
false;
2041 compuLoad_->sizeBase = sizeBase;
2042 compuLoad_->useAllocation =
true;
2052 std::promise<void> notYetTriggered;
2053 signalDone_.swap (notYetTriggered);
2054 return signalDone_.get_future();
2058 awaitBlocking(std::future<void> signal)
2060 if (std::future_status::timeout == signal.wait_for (
SAFETY_TIMEOUT))
2061 throw err::Fatal(
"Timeout on Scheduler test exceeded.");
2065 calcJob (
size_t idx,
size_t level)
2067 return Job{*calcFunctor_
2068 , calcFunctor_->encodeNodeID(idx)
2069 , calcFunctor_->encodeLevel(level)
2074 planningJob (
size_t endNodeIDX)
2076 return Job{*planFunctor_
2077 , planFunctor_->encodeNodeID(endNodeIDX)
2087 signalDone_.set_value();
2089 return Job{ wakeUpFun
2096 guessPlanningPreroll()
2098 return microseconds(_raw(
Time{chunkSize_ / planSpeed_}));
2104 return FrameRate{levelSpeed_ * blockLoadFac_};
2108 calcNextChunkEnd (
size_t lastNodeIDX)
2110 lastNodeIDX += chunkSize_;
2111 return min (lastNodeIDX, chainLoad_.size()-1);
2117 Time anchor = RealClock::now() + _uTicks(preRoll_);
2118 if (isnil (startTimes_))
2119 fillDefaultSchedule();
2120 size_t numPoints = chainLoad_.topLevel()+2;
2121 ENSURE (startTimes_.size() == numPoints);
2122 Offset base{startTimes_.front(), anchor};
2123 for (
size_t level=0; level<numPoints; ++level)
2124 startTimes_[level] += base;
2129 fillDefaultSchedule()
2131 size_t numPoints = chainLoad_.topLevel()+2;
2133 startTimes_.clear();
2134 startTimes_.reserve (numPoints);
2135 for (
size_t level=0; level<numPoints; ++level)
2136 startTimes_.push_back (level / levelSpeed_);
2140 fillAdaptedSchedule (
double stressFact, uint concurrency)
2142 REQUIRE (stressFact > 0);
2143 stressFac_ = stressFact;
2144 size_t numPoints = chainLoad_.topLevel()+2;
2145 startTimes_.clear();
2146 startTimes_.reserve (numPoints);
2147 startTimes_.push_back (Time::ZERO);
2148 chainLoad_.levelScheduleSequence (concurrency)
2149 .transform([&](
double scheduleFact){
return (scheduleFact/stressFac_) *
Offset{1,levelSpeed_};})
2150 .effuse(startTimes_);
2154 jobStartTime (
size_t level,
size_t nodeIDX =0)
2156 ENSURE (level < startTimes_.size());
2157 return startTimes_[level]
2158 + nodeExpense_ * (nodeIDX/stressFac_);
2162 lastExitNodes (
size_t lastChunkStartIDX)
2164 return chainLoad_.allExitNodes()
2165 .transform([&](
Node& n){
return chainLoad_.nodeID(n); })
2166 .
filter([=](
size_t idx){
return idx >= lastChunkStartIDX; });
2170 calcPlanScheduleTime (
size_t lastNodeIDX)
2178 lastNodeIDX = min (lastNodeIDX, chainLoad_.size()-1);
2179 size_t nextChunkLevel = chainLoad_.nodes_[lastNodeIDX].level;
2180 nextChunkLevel = nextChunkLevel>2? nextChunkLevel-2 : 0;
2181 return jobStartTime(nextChunkLevel) - _uTicks(preRoll_);
2190 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.
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.
TestChainLoad && setSeed(size_t seed=rand())
Set the overall seed value.
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
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