From 602b7dbe3a6553b1d25e32adb638533ffa9df6e4 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 11 Feb 2024 03:53:42 +0100 Subject: [PATCH] Scheduler-test: continue investigation - combine base expense with stress factor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After an extended break due to "real life issues".... Pick up the investigation, with the goal to ascertain a valid definition and understanding of all test parameters. A first step is to establish a baseline ''without using a computational load''; this might be some kind of base overhead of the scheduler. However -- the way the test scaffolding was built, it is difficult to create a feedback loop for the statistical test setup with binary search, since it is not really clear how the single control parameter of the test algorithm, the so called "stress factor", shall be interpreted and how it can be combined with a base load. An extended series of tests, while watching the observed value patterns qualitatively, seems to corroborate the former results, indicating that the base expense in my test setup (using a debug build) is at ~200µs / Node / core. Yet the difficulty to interpret this result and arrive at a logical and generic model prevents me from translating this into a measurement scheme, which can be executed independently from a specific test setup and hardware --- src/lib/iter-explorer.hpp | 8 +- src/lib/time/timevalue.hpp | 8 +- tests/vault/gear/scheduler-stress-test.cpp | 10 +- tests/vault/gear/stress-test-rig.hpp | 2 +- tests/vault/gear/test-chain-load.hpp | 15 +-- wiki/thinkPad.ichthyo.mm | 109 ++++++++++++++++++++- 6 files changed, 132 insertions(+), 20 deletions(-) diff --git a/src/lib/iter-explorer.hpp b/src/lib/iter-explorer.hpp index 92dbd897b..11effb0df 100644 --- a/src/lib/iter-explorer.hpp +++ b/src/lib/iter-explorer.hpp @@ -981,16 +981,16 @@ namespace lib { * already been exhausted. The aggregate is default-initialised at start of each group * and then the computation functor \a FAGG is invoked for each consecutive element marked * with the same _grouping value_ — and this grouping value itself is obtained by invoking - * the functor \a FGRP on each source value. No capturing or even reordering of the source - * elements takes place, rather groups are formed based on the changes of the grouping value - * over the source iterator's result sequence. + * the functor \a FGRP on each source value. All computation are performed on-the-fly. No + * capturing or reordering of the source elements takes place, rather groups are formed + * based on the changes of the grouping value over the source iterator's result sequence. * @tparam AGG data type to collect the aggregate; must be default constructible and assignable * @tparam GRP value type to indicate a group * @note while `groupFun` is adapted, the `aggFun` is _not adapted_ to the source iterator, * but expected always to take the _value type_ of the preceding iterator, i.e. `*srcIter`. * This limitation was deemed acceptable (adapting a function with several arguments would * require quite some nasty technicalities). The first argument of this `aggFun` refers - * to the accumulator by value, and thereby also implititly defines the aggregate result type. + * to the accumulator by value, and thereby also implicitly defines the aggregate result type. */ template class GroupAggregator diff --git a/src/lib/time/timevalue.hpp b/src/lib/time/timevalue.hpp index 065101877..a6d977165 100644 --- a/src/lib/time/timevalue.hpp +++ b/src/lib/time/timevalue.hpp @@ -541,16 +541,16 @@ namespace time { : Duration::NIL; } - template + template inline Offset - operator* (INT factor, Duration const& dur) + operator* (NUM factor, Duration const& dur) { return factor * Offset(dur); } - template + template inline Offset - operator* (Duration const& dur, INT factor) + operator* (Duration const& dur, NUM factor) { return factor*dur; } diff --git a/tests/vault/gear/scheduler-stress-test.cpp b/tests/vault/gear/scheduler-stress-test.cpp index 0d783226f..7aa3eac9f 100644 --- a/tests/vault/gear/scheduler-stress-test.cpp +++ b/tests/vault/gear/scheduler-stress-test.cpp @@ -329,7 +329,7 @@ namespace test { investigateWorkProcessing() { MARK_TEST_FUN - TestChainLoad<8> testLoad{64}; + TestChainLoad<8> testLoad{256}; testLoad.seedingRule(testLoad.rule().probability(0.6).minVal(2)) .pruningRule(testLoad.rule().probability(0.44)) .setSeed(55) @@ -369,7 +369,11 @@ SHOW_EXPR(loadMicros) { usec LOAD_BASE = 500us; usec BASE_EXPENSE = 200us; - double UPPER_STRESS = 8; + double UPPER_STRESS = 12; + // + double FAIL_LIMIT = 0.7; + double TRIGGER_SDEV = 0.25; + double TRIGGER_DELTA = 0.5; // uint CONCURRENCY = 4; // bool SCHED_DEPENDS = true; bool showRuns = true; @@ -377,7 +381,7 @@ SHOW_EXPR(loadMicros) auto testLoad() { - TestChainLoad<8> testLoad{64}; + TestChainLoad<8> testLoad{256}; testLoad.seedingRule(testLoad.rule().probability(0.6).minVal(2)) .pruningRule(testLoad.rule().probability(0.44)) .setSeed(55); diff --git a/tests/vault/gear/stress-test-rig.hpp b/tests/vault/gear/stress-test-rig.hpp index 72ace65dc..7488cb38d 100644 --- a/tests/vault/gear/stress-test-rig.hpp +++ b/tests/vault/gear/stress-test-rig.hpp @@ -340,7 +340,7 @@ namespace test { 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 delta above this limit + 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 diff --git a/tests/vault/gear/test-chain-load.hpp b/tests/vault/gear/test-chain-load.hpp index 4e5c7d8b2..9ca97197d 100644 --- a/tests/vault/gear/test-chain-load.hpp +++ b/tests/vault/gear/test-chain-load.hpp @@ -198,7 +198,7 @@ namespace test { const bool SCHED_NOTIFY = true; ///< explicitly set notify dispatch time to the dependency's start time. inline uint - defaultConcurr() + defaultConcurrency() { return work::Config::getDefaultComputationCapacity(); } @@ -1675,6 +1675,7 @@ namespace test { FrameRate levelSpeed_{1, SCHEDULE_LEVEL_STEP}; FrameRate planSpeed_{1, SCHEDULE_PLAN_STEP}; TimeVar nodeExpense_{SCHEDULE_NODE_STEP}; + double stressFact_{1.0}; bool schedNotify_{SCHED_NOTIFY}; bool schedDepends_{SCHED_DEPENDS}; uint blockLoadFactor_{2}; @@ -1806,7 +1807,7 @@ namespace test { getExpectedEndTime() { return _raw(startTimes_.back() - startTimes_.front() - + Duration{nodeExpense_}*chainLoad_.size()); + + Duration{nodeExpense_}*(chainLoad_.size()/stressFact_)); } @@ -1881,8 +1882,8 @@ namespace test { withAdaptedSchedule (double stressFac =1.0, uint concurrency=0) { if (not concurrency) // use hardware concurrency (#cores) by default - concurrency = defaultConcurr(); - ENSURE (isLimited (1u, concurrency, 3*defaultConcurr())); + concurrency = defaultConcurrency(); + ENSURE (isLimited (1u, concurrency, 3*defaultConcurrency())); withLevelDuration (compuLoad_->timeBase); fillAdaptedSchedule (stressFac, concurrency); return move(*this); @@ -2024,6 +2025,7 @@ namespace test { fillDefaultSchedule() { size_t numPoints = chainLoad_.topLevel()+2; + stressFact_ = 1.0; startTimes_.clear(); startTimes_.reserve (numPoints); for (size_t level=0; level 0); + stressFact_ = stressFact; size_t numPoints = chainLoad_.topLevel()+2; startTimes_.clear(); startTimes_.reserve (numPoints); startTimes_.push_back (Time::ZERO); chainLoad_.levelScheduleSequence (concurrency) - .transform([&](double scheduleFact){ return (scheduleFact/stressFact) * Offset{1,levelSpeed_};}) + .transform([&](double scheduleFact){ return (scheduleFact/stressFact_) * Offset{1,levelSpeed_};}) .effuse(startTimes_); } @@ -2048,7 +2051,7 @@ namespace test { { ENSURE (level < startTimes_.size()); return startTimes_[level] - + nodeExpense_ * nodeIDX; + + nodeExpense_ * (nodeIDX/stressFact_); } auto diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 98a9f3758..ffae2a668 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -110893,7 +110893,9 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + + @@ -110995,9 +110997,112 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + + + + + +

+ ...das ist eine inhärente Limitierung, die ich jedoch als Grenzfall und damit nicht relevant betrachte; zum einen sind dann meistens die Laufzeiten so kurz, daß die statistischen Triggerschwellen gar nicht greifen können, und außerdem, wenn das generierte Schedule gar keine zeitliche Komponente enthält — also alle Jobs gleichzeitig starten sollen — dann läßt sich auch nichts verdichten und der binäre Suchalgorithmus konvergiert auf das erste gefundene Rand-Intervall +

+ +
+ +
+ + + + + + + + + + +

+ das ist eine im Rückblick ungeschickte Vereinfachung, die ich ganz zu Beginn für den SchedulerCtx gemacht habe; eigentlich müßte man die Implementierung jetzt wergwerfen und nochmal systematisch von Null auf neu aufbauen. Das versuche ich zu vermeiden, da es sich letztlich nur um Test / Expermental-Code handelt, und zudem gegenwärtig nur eine erste Grobeinschätzung durch die Streß-Tests gefordert ist (ich gehe davon aus, daß das ganze Test-Setup später für detailiertere Messungen ohnehin noch einmal neu implementiert werden muß, wenn wir denn auch dafür Zeit haben) +

+ +
+ +
+ + + + + + + + + + + + + + + + + + + +

+ Die Mantisse des Double-Datentyps ist 24bit lang. Das reicht nicht, um einen 64bit-Integer fehlerfrei mit voller Genauigkeit darzustellen +

+ +
+
+ + + +
+
+
+ + + + + + + +

+ das heißt, es gibt einen Punkt ab dem die Standardabweichung deutlich ansteigt, und viele Einzel-Läufe deutlich länger brauchen +

+ +
+ +
+ + + + + + + + + + + + + + + + +

+ Er liegt tatsächlich etwas niedriger, nämlich 180µs ±5µs. Die Zahlen sind erstaunlich stabil und relativ genau, und passen auch mit dem statistischen Verhalten zusammen; insofer betrachte ich die 5.8ms Gesamtlaufzeit als einen empirischen Fakt. Und es paßt in's Bild, daß dieser Wert etwas niedriger liegt für eine lange Kette (Cache-Effekte) +

+ +
+
+
+
+ + + + +