diff --git a/tests/vault/gear/scheduler-stress-test.cpp b/tests/vault/gear/scheduler-stress-test.cpp index 9c9f59cbe..087af3c09 100644 --- a/tests/vault/gear/scheduler-stress-test.cpp +++ b/tests/vault/gear/scheduler-stress-test.cpp @@ -81,8 +81,9 @@ namespace test { { //smokeTest(); // setup_systematicSchedule(); - search_breaking_point(); // verify_instrumentation(); +// search_breaking_point(); + watch_expenseFunction(); // investigateWorkProcessing(); walkingDeadline(); } @@ -328,16 +329,22 @@ namespace test { - /** @test TODO determine the breaking point towards scheduler overload + /** @test determine the breaking point towards scheduler overload * - use the integrated StressRig * - demonstrate how parameters can be tweaked * - perform a run, leading to a binary search for the breaking point - * @note on my machine, I observe stress factors close below 0.5, due to the fact - * that the ComputationalLoad typically takes 2 times as long in concurrent - * usage compared to its calibration, which is done in a tight loop. This - * is strange and may well be due to some peculiarity of my system. Which - * also implies that this test's behaviour might be difficult to verify, - * other than by qualitative interpretation of the log output on STDOUT. + * @remark this stress-test setup uses instrumentation internally note to deduce + * some systematic deviations from the theoretically established behaviour. + * For example, on my machine, the ComputationalLoad performs slower within the + * Scheduler environment compared to its calibration, which is done in a tight loop. + * This may be due to internals of the processor, which show up under increased + * contention combined with more frequent cache misses. In a similar vein, the + * actually observed concurrency turns out to be consistently lower than could + * be expected by accounting for the work units in isolation, without considering + * dependency constraints. These observed deviations are cast into an empirical + * »form factor«, which is then used to correct the applied stress factor. + * Only with taking these corrective steps, the observed stress factor at + * _breaking point_ comes close to the theoretically expected value of 1.0 * @see stress-test-rig.hpp * @todo WIP 1/24 ✔ define ⟶ ✔ implement */ @@ -363,6 +370,41 @@ namespace test { + /** @test TODO Investigate the relation of run time (expense) to input length. + * @see vault::gear::bench::ParameterRange + * @todo WIP 1/24 🔁 define ⟶ 🔁 implement + */ + void + watch_expenseFunction() + { + ComputationalLoad cpuLoad; + cpuLoad.timeBase = 200us; + cpuLoad.calibrate(); +//////////////////////////////////////////////////////////////////TODO for development only + MARK_TEST_FUN + + struct Setup : StressRig + { + usec LOAD_BASE = 500us; + uint CONCURRENCY = 4; + bool showRuns = true; + + auto testLoad(size_t nodes) + { + TestChainLoad<8> testLoad{nodes}; + return testLoad.seedingRule(testLoad.rule().probability(0.6).minVal(2)) + .pruningRule(testLoad.rule().probability(0.44)) + .weightRule(testLoad.value(1)) + .setSeed(55); + } + }; + + auto results = StressRig::with() + .perform (2,64); + } + + + /** @test TODO * @todo WIP 1/24 🔁 define ⟶ implement */ diff --git a/tests/vault/gear/stress-test-rig.hpp b/tests/vault/gear/stress-test-rig.hpp index 715909f54..1150886e8 100644 --- a/tests/vault/gear/stress-test-rig.hpp +++ b/tests/vault/gear/stress-test-rig.hpp @@ -93,7 +93,7 @@ #include "lib/meta/function.hpp" #include "lib/format-string.hpp" #include "lib/format-cout.hpp"//////////////////////////TODO RLY? -//#include "lib/util.hpp" +#include "lib/util.hpp" //#include #include @@ -194,9 +194,9 @@ namespace test { protected: /** Extension point: build the computation topology for this test */ auto - testLoad() + testLoad(size_t nodes =64) { - return TestChainLoad<>{64}; + return TestChainLoad<>{nodes}; } /** (optional) extension point: base configuration of the test ScheduleCtx */ @@ -431,27 +431,62 @@ namespace test { class ParameterRange : public CONF { - using TestLoad = decltype(declval().testLoad()); + using TestLoad = decltype(declval().testLoad(1)); using TestSetup = decltype(declval().testSetup (declval())); + template + using Point = std::pair; + template + void + runTest (Point& 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) + .withAdaptedSchedule(stressFac, CONF::CONCURRENCY); + point.second = testSetup.launch_and_wait() / 1000; +cout << "x="< erster Versuch: überhaupt keinen Graph konstruieren

- - + @@ -111176,8 +111175,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
fast alle Threads sind im Contention-wait

- - +
@@ -111187,8 +111185,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
das ist zwar ganz klar geplant, aber ich jetzt lasse ich die Tests erst mal so laufen...

- -
+
@@ -111199,8 +111196,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
Zwar beobachte ich die Memory-Corruption dann ehr beim Einfügen der Events, aber das kann sehr wohl ein Folge-Fehler sein. Ich sehe auch anderes Verhalten, das „eigentlich gar nicht möglich ist“

- - + @@ -111210,8 +111206,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
Hier passiert ein Vector-realloc. Wenn währenddessen der nächste Thread kommt, macht der ebenfalls ein re-Alloc und die Memory-corruption wäre garantiert.

- -
+
@@ -111222,8 +111217,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
Das ist ein Instrumentierungs-Hilfsmittel, und generell nur darauf ausgelegt, »richtig verwendet zu werden«

- - +
@@ -111273,8 +111267,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
...waren aber geblockt, solange noch die eigentlichen Jobs mit t=0 in der Queue stehen

- - +
@@ -111295,8 +111288,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
...und die Selbstreguliertung sorgt schntell dafür, daß der größte Teil der WorkForce in contention-Wait geht

- - +
@@ -111311,8 +111303,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
zweiter Versuch: unconnected Nodes with weight (500µs)

- - + @@ -111333,8 +111324,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
das entspricht exakt der Concurrency

- - +
@@ -111357,8 +111347,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
das ist effektiv concurrency ≡ 6

- - +
@@ -111389,8 +111378,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
mit 400 Nodes  ⟼ real ∅ 12ms und Concurrency ≡ 7.9

- - +
@@ -111414,8 +111402,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
...weil keine Festlegung auf eine bestimmte Zahl an Cores möglich ist und auch die Werte stark statistisch schwanken, besonders bei den relativ kurzen Laufzeiten, die hier aus praktischen Gründen erforderlich sind, schon um das Test-Setup einfach zu halten

- - +
@@ -111431,8 +111418,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
Stichwort: Box stacking. Das ist ein NP-hartes Problem und wir lösen es hier nicht; vielmehr wird so getan, als könnte man um die durchschnittliche Concurrency elastisch stauchen. Das führt dann zum Auftreten eines weiteren »Form-Faktors«, der eben genau darin besteht, zu welchem Grad diese Annahme zu den strukturellen Beschränkugeneni im Widerspruch steht.

- - +
@@ -111476,8 +111462,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - +
@@ -111500,8 +111485,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
Mehraufwand ⟹ bereits ein entsprechend kleinerer Stress-Faktor hat die gleiche Stress-Wirkung

- - +
@@ -111524,8 +111508,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
...das läßt dann die nominelle Concurrency als integral-Zahl intakt

- - +
@@ -111603,8 +111586,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
denn das Schedule wird ja auf eine nominelle Concurrency ausgelegt

- - +
@@ -111614,8 +111596,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
und diese wird ggfs aus strukturellen Gründen nicht ausgeschöpft

- -
+
@@ -111631,8 +111612,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
...die Berechnung läuft zwar genauso, nämlich über eine Gruppierung per Level, jedoch müssen dann nur die reinen Nodes pro Level berücksichtigt werden

- - +
@@ -111642,8 +111622,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
weil die Gewichte entsprechend proportional auch in die durchschnittliche empirische Concurrency eingehen

- -
+
@@ -111690,8 +111669,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
ohne starke Abhängigkeiten ist durchaus elastisches Verhalten denkbar

- - + @@ -111699,8 +111677,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
Zwar ist die nun entwickelte Meßmethode durchaus elegant, wird aber zunehmend ungenau, wenn es kein klar definiertes »Brechen« mehr gibt, bzw. wenn man nur noch einen numerischen Grenzwert festlegen kann, der dann von den Dimensionen des konketen Falls abhängen. Für das wohldefinierte »Brechen« ist eine komplexe Abhängikeitsstruktur notewendig, die durch einen Rückstau komplett aus der Balance kippt. Das ist dann doch ein sehr spezieller Fall, denn in der Praxis erwarte ich im Durchschnitt ehr viele kleine 2-er und 3-er-Ketten ohne viel Verknüfpungen (ein IO-Job und ein Render-Job). Und bei länger laufenden Berechnungen wäre definitiv zu erwarten (ja sogar zwingend gefordert), daß sich freie Kapazität elastisch auf die nächsten Aufgaben verschieben läßt.

- -
+
@@ -111710,8 +111687,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
Der Scheduler ist ein komplexer Mechanismus, und allein das Hochfahren des Worker-Pools, alsauch das Erstellen des initialen Schedule erzeugt einen festen Overhead, der mehr oder weniger unkontrolliert in den Start der Arbeitsphase hineinwirkt. Je nachdem wann und wo der erste Eintrag im Schedule auftaucht, erscheinen zu einem bestimmten Zeitpunkt alle Worker und sorgen erst mal für deutlich meßbare Contention-Verzögerungen. Des Weiteren erzeugt auch die Abhängigkeit auf den Wake-up-Job einen erheblichen Overhead, sobald es nicht mehr einen einzigen stringenten Berchnungspfad gibt. Hinzu kommt, daß wir nicht auf einem Realtime-Betriebssystem arbeiten, und die vermutete Basis-Last stark von der Optimierung abhängig ist. Und außerdem hat der Scheduler nun einen speziellen Schutzmechanismus, der den Worker-Pool beim Auftreten von Contention effektiv herunterregelt. All dies zusammen macht eine Beobachtung des »Leerlauf-Aufwandes« sehr schwer...

- -
+
@@ -111721,8 +111697,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
Nebenbei sollten sich dabei auch nicht-lineare Basis-Effekte zeigen, aber idealerweise sollte das Verhalten ab einem gewissen Punkt linear werden; aus diesem näherungsweise linearen Verhalten ließe sich auf theoretischem Weg auch ein hypothetischer / amortisierter Basis-Aufwand ableiten. Die Hoffung ist, daß durch den Abgleich dieser verschiedenen Meßmethoden ein klareres Verständnis der Grenzen der Meßanordnung gewonnen werden kann.

- -
+
@@ -111732,8 +111707,23 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + + + + + + + + +

+ verschiedene Parameter-Werte führen zu besserer statistischer Abdeckung +

+ + +
+
@@ -111742,7 +111732,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + @@ -111750,9 +111741,10 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + - + @@ -111764,10 +111756,45 @@ Date:   Thu Apr 20 18:53:17 2023 +0200

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -112178,8 +112205,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
das beobachte ich mit dem DUMP-Logging immer wieder. Und zwar ohne eine ausgeprägte Contention-Spitze. Nach dem Absetzen des letzten regulären Node-Jobs sieht man das Log vom Continuation-Aufruf, dann lang nichts, dann die Sequenz der dependencies.

- - +