Scheduler-test: integrate instrumentation as optional feature

...can be activated on the Test-Chain-Load
...add a test case to validate its operation
This commit is contained in:
Fischlurch 2024-02-15 00:43:03 +01:00
parent 580c1f1f68
commit 3e1239bd71
5 changed files with 220 additions and 152 deletions

View file

@ -212,7 +212,7 @@ namespace lib {
* and then compute statistics evaluations to characterise observations.
* @warning caller must ensure there was a barrier or visibility sync before invocation.
*/
IncidenceCount::Statistic
inline IncidenceCount::Statistic
IncidenceCount::evaluate()
{
Statistic stat;

View file

@ -26,7 +26,6 @@
#include "lib/test/run.hpp"
#include "lib/test/diagnostic-output.hpp"//////////////TODO RLY?
#include "lib/test/microbenchmark.hpp"
#include "lib/incidence-count.hpp"
#include "lib/thread.hpp"
@ -76,7 +75,6 @@ namespace test{
/** @test watch time spent in code bracketed by measurement calls.
* @todo WIP 2/24 define implement
*/
void
demonstrate_usage()
@ -98,9 +96,7 @@ namespace test{
}
/** @test verify proper counting of possibly overlapping incidences
* @todo WIP 2/24 define implement
*/
/** @test verify proper counting of possibly overlapping incidences. */
void
verify_incidentCount()
{
@ -126,56 +122,35 @@ namespace test{
watch.markLeave(1);
auto stat = watch.evaluate();
SHOW_EXPR(stat.cumulatedTime);
SHOW_EXPR(stat.coveredTime);
SHOW_EXPR(stat.activeTime);
SHOW_EXPR(stat.eventCnt);
SHOW_EXPR(stat.activationCnt);
SHOW_EXPR(stat.cntCase(0));
SHOW_EXPR(stat.cntCase(1));
SHOW_EXPR(stat.cntCase(2));
SHOW_EXPR(stat.cntCase(3));
SHOW_EXPR(stat.cntCase(4));
SHOW_EXPR(stat.timeCase(0));
SHOW_EXPR(stat.timeCase(1));
SHOW_EXPR(stat.timeCase(2));
SHOW_EXPR(stat.timeCase(3));
SHOW_EXPR(stat.timeCase(4));
SHOW_EXPR(stat.cntThread(0));
SHOW_EXPR(stat.cntThread(1));
SHOW_EXPR(stat.timeThread(0));
SHOW_EXPR(stat.timeThread(1));
CHECK (isLimited (15500, stat.cumulatedTime, 17800)); // ≈ 16ms
CHECK (isLimited ( 8500, stat.coveredTime, 10000)); // ≈ 9ms
CHECK (10== stat.eventCnt);
CHECK (5 == stat.activationCnt);
CHECK (0 == stat.cntCase(0));
CHECK (2 == stat.cntCase(1));
CHECK (1 == stat.cntCase(2));
CHECK (2 == stat.cntCase(3));
CHECK (0 == stat.cntCase(4));
CHECK (0 == stat.timeCase(0));
CHECK (isLimited ( 5500, stat.timeCase(1), 6800)); // ≈ 6ms
CHECK (isLimited ( 3500, stat.timeCase(2), 4500)); // ≈ 4ms
CHECK (isLimited ( 5500, stat.timeCase(3), 6800)); // ≈ 6ms
CHECK (0 == stat.timeCase(4));
CHECK (5 == stat.cntThread(0));
CHECK (0 == stat.cntThread(1));
CHECK (stat.activeTime == stat.timeThread(0));
CHECK (0 == stat.timeThread(1));
CHECK (isNumEq (stat.activeTime, stat.coveredTime));
CHECK (isNumEq (stat.cumulatedTime , stat.timeCase(1) + stat.timeCase(2) + stat.timeCase(3)));
CHECK (isLimited (15500, stat.cumulatedTime, 17800)); // ≈ 16ms
CHECK (isLimited ( 8500, stat.coveredTime, 10000)); // ≈ 9ms
CHECK (10== stat.eventCnt);
CHECK (5 == stat.activationCnt);
CHECK (0 == stat.cntCase(0));
CHECK (2 == stat.cntCase(1));
CHECK (1 == stat.cntCase(2));
CHECK (2 == stat.cntCase(3));
CHECK (0 == stat.cntCase(4));
CHECK (0 == stat.timeCase(0));
CHECK (isLimited ( 5500, stat.timeCase(1), 6800)); // ≈ 6ms
CHECK (isLimited ( 3500, stat.timeCase(2), 4500)); // ≈ 4ms
CHECK (isLimited ( 5500, stat.timeCase(3), 6800)); // ≈ 6ms
CHECK (0 == stat.timeCase(4));
CHECK (5 == stat.cntThread(0));
CHECK (0 == stat.cntThread(1));
CHECK (stat.activeTime == stat.timeThread(0));
CHECK (0 == stat.timeThread(1));
CHECK (isNumEq (stat.activeTime, stat.coveredTime));
CHECK (isNumEq (stat.cumulatedTime , stat.timeCase(1) + stat.timeCase(2) + stat.timeCase(3)));
}
/** @test verify observation of concurrency degree
* @todo WIP 2/24 define implement
*/
/** @test verify observation of concurrency degree. */
void
verify_concurrencyStatistic()
{
MARK_TEST_FUN
IncidenceCount watch;
watch.expectThreads(2)
.expectIncidents(2);
@ -202,31 +177,7 @@ SHOW_EXPR(stat.timeThread(1));
// join ensures visibility of all data changes from within threads,
// which is a prerequisite for performing the data evaluation safely.
auto stat = watch.evaluate();
SHOW_EXPR(runTime)
SHOW_EXPR(stat.cumulatedTime);
SHOW_EXPR(stat.activeTime);
SHOW_EXPR(stat.coveredTime);
SHOW_EXPR(stat.eventCnt);
SHOW_EXPR(stat.activationCnt);
SHOW_EXPR(stat.cntCase(0));
SHOW_EXPR(stat.cntCase(1));
SHOW_EXPR(stat.cntCase(2));
SHOW_EXPR(stat.cntCase(3));
SHOW_EXPR(stat.timeCase(0));
SHOW_EXPR(stat.timeCase(1));
SHOW_EXPR(stat.timeCase(2));
SHOW_EXPR(stat.timeCase(3));
SHOW_EXPR(stat.cntThread(0));
SHOW_EXPR(stat.cntThread(1));
SHOW_EXPR(stat.cntThread(2));
SHOW_EXPR(stat.timeThread(0));
SHOW_EXPR(stat.timeThread(1));
SHOW_EXPR(stat.timeThread(2));
SHOW_EXPR(stat.avgConcurrency);
SHOW_EXPR(stat.timeAtConc(0));
SHOW_EXPR(stat.timeAtConc(1));
SHOW_EXPR(stat.timeAtConc(2));
SHOW_EXPR(stat.timeAtConc(3));
CHECK (runTime > stat.coveredTime);
CHECK (stat.coveredTime < stat.cumulatedTime);
CHECK (stat.activeTime <= stat.cumulatedTime);
@ -259,13 +210,11 @@ SHOW_EXPR(stat.timeAtConc(3));
}
/** @test TODO verify thread-safe operation under pressure
* @todo WIP 2/24 define implement
*/
/** @test verify thread-safe operation under pressure. */
void
perform_multithreadStressTest()
{
MARK_TEST_FUN
constexpr size_t CONCURR = 16;
const size_t REPETITIONS = 100;
@ -283,76 +232,32 @@ SHOW_EXPR(stat.timeAtConc(3));
watch.markLeave();
};
// Invoke these two nested activations numerous times in several threads
auto [runTime, sum] = test::threadBenchmark<CONCURR> (act, REPETITIONS);
SHOW_EXPR(runTime)
SHOW_EXPR(sum)
CHECK (1600 == sum);
CHECK (sum == CONCURR*REPETITIONS); // each invocation contributes +1
CHECK (isLimited (900, runTime, 1400)); // delay is 500µs on average
// compute statistics over recorded events
auto stat = watch.evaluate();
SHOW_EXPR(stat.cumulatedTime);
SHOW_EXPR(stat.activeTime);
SHOW_EXPR(stat.coveredTime);
// on average two times 500µs per invocation
CHECK (isLimited (900*REPETITIONS, stat.coveredTime, 1400*REPETITIONS));
CHECK (stat.activeTime > 900 * REPETITIONS * CONCURR);
SHOW_EXPR(stat.eventCnt);
SHOW_EXPR(stat.activationCnt);
CHECK (stat.activeTime > 900 * REPETITIONS*CONCURR);
CHECK (stat.activationCnt == 2*REPETITIONS*CONCURR);
SHOW_EXPR(stat.cntCase(0));
CHECK (stat.cntCase(0) == REPETITIONS*CONCURR);
SHOW_EXPR(stat.cntCase(1));
CHECK (stat.cntCase(1) == 0);
SHOW_EXPR(stat.cntCase(2));
CHECK (stat.cntCase(2) == REPETITIONS*CONCURR);
SHOW_EXPR(stat.cntCase(3));
SHOW_EXPR(stat.timeCase(0));
SHOW_EXPR(stat.timeCase(1));
SHOW_EXPR(stat.timeCase(2));
SHOW_EXPR(stat.timeCase(3));
SHOW_EXPR(stat.cntThread(0));
SHOW_EXPR(stat.cntThread(1));
SHOW_EXPR(stat.cntThread(2));
SHOW_EXPR(stat.cntThread(3));
SHOW_EXPR(stat.cntThread(4));
SHOW_EXPR(stat.cntThread(5));
SHOW_EXPR(stat.cntThread(6));
SHOW_EXPR(stat.cntThread(7));
SHOW_EXPR(stat.cntThread(8));
SHOW_EXPR(stat.cntThread(9));
SHOW_EXPR(stat.timeThread(0));
SHOW_EXPR(stat.timeThread(1));
SHOW_EXPR(stat.timeThread(2));
SHOW_EXPR(stat.timeThread(3));
SHOW_EXPR(stat.timeThread(4));
SHOW_EXPR(stat.timeThread(5));
SHOW_EXPR(stat.timeThread(6));
SHOW_EXPR(stat.timeThread(7));
SHOW_EXPR(stat.timeThread(8));
SHOW_EXPR(stat.timeThread(9));
SHOW_EXPR(stat.avgConcurrency);
CHECK (isLimited(CONCURR/2, stat.avgConcurrency, CONCURR));
// if there are enough cores, ∅ concurrency should even be close to CONCURR
for (uint i=0; i<CONCURR; ++i)
CHECK (isLimited(REPETITIONS*900, stat.timeThread(i), REPETITIONS*1400));
CHECK (0 == stat.timeThread(CONCURR));
CHECK (0 == stat.timeAtConc(CONCURR+1));
SHOW_EXPR(stat.timeAtConc(0));
SHOW_EXPR(stat.timeAtConc(1));
SHOW_EXPR(stat.timeAtConc(2));
SHOW_EXPR(stat.timeAtConc(3));
SHOW_EXPR(stat.timeAtConc(4));
SHOW_EXPR(stat.timeAtConc(5));
SHOW_EXPR(stat.timeAtConc(6));
SHOW_EXPR(stat.timeAtConc(7));
SHOW_EXPR(stat.timeAtConc(8));
SHOW_EXPR(stat.timeAtConc(9));
SHOW_EXPR(stat.timeAtConc(10));
SHOW_EXPR(stat.timeAtConc(11));
SHOW_EXPR(stat.timeAtConc(12));
SHOW_EXPR(stat.timeAtConc(13));
SHOW_EXPR(stat.timeAtConc(14));
SHOW_EXPR(stat.timeAtConc(15));
SHOW_EXPR(stat.timeAtConc(16));
SHOW_EXPR(stat.timeAtConc(17));
CHECK (0 == stat.timeThread(CONCURR)); // there should not be any idle time recorded
CHECK (0 == stat.timeAtConc(CONCURR+1)); // there should be never more concurrency than number of threads
// most of the time, concurrency should be close to the defined maximum
CHECK (isLimited(REPETITIONS*900, stat.timeAtConc(CONCURR), REPETITIONS*1200));
}
};

View file

@ -82,7 +82,8 @@ namespace test {
//smokeTest();
// setup_systematicSchedule();
// search_breaking_point();
investigateWorkProcessing();
verify_instrumentation();
// investigateWorkProcessing();
walkingDeadline();
}
@ -287,6 +288,96 @@ namespace test {
/** @test TODO verify detailed instrumentation of job invocations
* @todo WIP 2/24 🔁 define implement
*/
void
verify_instrumentation()
{
MARK_TEST_FUN
TestChainLoad testLoad{20};
testLoad
.printTopologyDOT()
.printTopologyStatistics()
;
BlockFlowAlloc bFlow;
EngineObserver watch;
Scheduler scheduler{bFlow, watch};
auto LOAD_BASE = 500us;
// auto stressFac = 1.0;
// auto concurrency = 8;
//
ComputationalLoad cpuLoad;
cpuLoad.timeBase = LOAD_BASE;
cpuLoad.calibrate();
//
double loadMicros = cpuLoad.invoke();
double refTime = testLoad.calcRuntimeReference(LOAD_BASE);
SHOW_EXPR(loadMicros)
auto testSetup =
testLoad.setupSchedule(scheduler)
.withLoadTimeBase(LOAD_BASE)
.withJobDeadline(50ms)
.withUpfrontPlanning()
.withInstrumentation()
// .withAdaptedSchedule (stressFac, concurrency)
;
double runTime = testSetup.launch_and_wait();
double expected = testSetup.getExpectedEndTime();
SHOW_EXPR(runTime)
SHOW_EXPR(expected)
SHOW_EXPR(refTime)
auto stat = testSetup.getInvocationStatistic();
SHOW_EXPR(stat.cumulatedTime);
SHOW_EXPR(stat.activeTime);
SHOW_EXPR(stat.coveredTime);
SHOW_EXPR(stat.eventCnt);
SHOW_EXPR(stat.activationCnt);
SHOW_EXPR(stat.cntCase(0));
SHOW_EXPR(stat.cntCase(1));
SHOW_EXPR(stat.cntCase(2));
SHOW_EXPR(stat.cntCase(3));
SHOW_EXPR(stat.timeCase(0));
SHOW_EXPR(stat.timeCase(1));
SHOW_EXPR(stat.timeCase(2));
SHOW_EXPR(stat.timeCase(3));
SHOW_EXPR(stat.cntThread(0));
SHOW_EXPR(stat.cntThread(1));
SHOW_EXPR(stat.cntThread(2));
SHOW_EXPR(stat.cntThread(3));
SHOW_EXPR(stat.cntThread(4));
SHOW_EXPR(stat.cntThread(5));
SHOW_EXPR(stat.cntThread(6));
SHOW_EXPR(stat.cntThread(7));
SHOW_EXPR(stat.cntThread(8));
SHOW_EXPR(stat.cntThread(9));
SHOW_EXPR(stat.timeThread(0));
SHOW_EXPR(stat.timeThread(1));
SHOW_EXPR(stat.timeThread(2));
SHOW_EXPR(stat.timeThread(3));
SHOW_EXPR(stat.timeThread(4));
SHOW_EXPR(stat.timeThread(5));
SHOW_EXPR(stat.timeThread(6));
SHOW_EXPR(stat.timeThread(7));
SHOW_EXPR(stat.timeThread(8));
SHOW_EXPR(stat.timeThread(9));
SHOW_EXPR(stat.avgConcurrency);
SHOW_EXPR(stat.timeAtConc(0));
SHOW_EXPR(stat.timeAtConc(1));
SHOW_EXPR(stat.timeAtConc(2));
SHOW_EXPR(stat.timeAtConc(3));
SHOW_EXPR(stat.timeAtConc(4));
SHOW_EXPR(stat.timeAtConc(5));
SHOW_EXPR(stat.timeAtConc(6));
SHOW_EXPR(stat.timeAtConc(7));
SHOW_EXPR(stat.timeAtConc(8));
SHOW_EXPR(stat.timeAtConc(9));
}
/** @test TODO determine the breaking point towards scheduler overload
* - use the integrated StressRig
* - demonstrate how parameters can be tweaked

View file

@ -122,6 +122,7 @@
#include "vault/gear/special-job-fun.hpp"
#include "lib/uninitialised-storage.hpp"
#include "lib/test/microbenchmark.hpp"
#include "lib/incidence-count.hpp"
#include "lib/time/timevalue.hpp"
#include "lib/time/quantiser.hpp"
#include "lib/iter-explorer.hpp"
@ -1177,7 +1178,7 @@ namespace test {
for (uint i=0; i<CAT; ++i)
particulars[i] += classify[i](node);
}
ENSURE (level = topLevel());
ENSURE (level == topLevel());
detectSubgraphs();
stat.addPoint (width, sublevel, particulars);
stat.closeAverages (segs, maxsublevel);
@ -1549,14 +1550,17 @@ namespace test {
: public ChainFunctor
{
using Node = typename TestChainLoad<maxFan>::Node;
using Watch = lib::IncidenceCount;
Node* startNode_;
ComputationalLoad* compuLoad_;
Watch* watch_;
public:
RandomChainCalcFunctor(Node& startNode, ComputationalLoad* load =nullptr)
RandomChainCalcFunctor(Node& startNode, ComputationalLoad* load =nullptr, Watch* watch =nullptr)
: startNode_{&startNode}
, compuLoad_{load}
, watch_{watch}
{ }
@ -1564,6 +1568,7 @@ namespace test {
void
invokeJobOperation (JobParameter param) override
{
if (watch_) watch_->markEnter();
size_t nodeIdx = decodeNodeID (param.invoKey);
size_t level = decodeLevel (TimeValue{param.nominalTime});
Node& target = startNode_[nodeIdx];
@ -1572,6 +1577,7 @@ namespace test {
if (compuLoad_ and target.weight)
compuLoad_->invoke (target.weight);
target.calculate();
if (watch_) watch_->markLeave();
}
string diagnostic() const override
@ -1692,6 +1698,8 @@ namespace test {
std::unique_ptr<RandomChainCalcFunctor<maxFan>> calcFunctor_;
std::unique_ptr<RandomChainPlanFunctor<maxFan>> planFunctor_;
std::unique_ptr<lib::IncidenceCount> watchInvocations_;
/* ==== Callbacks from job planning ==== */
@ -1752,7 +1760,7 @@ namespace test {
size_t firstChunkEndNode = calcNextChunkEnd(0);
schedule_.allocate (numNodes);
compuLoad_->maybeCalibrate();
calcFunctor_.reset (new RandomChainCalcFunctor<maxFan>{chainLoad_.nodes_[0], compuLoad_.get()});
calcFunctor_.reset (new RandomChainCalcFunctor<maxFan>{chainLoad_.nodes_[0], compuLoad_.get(), watchInvocations_.get()});
planFunctor_.reset (new RandomChainPlanFunctor<maxFan>{chainLoad_.nodes_[0], chainLoad_.numNodes_
,[this](size_t i, size_t l){ disposeStep(i,l); }
,[this](auto* p, auto* s) { setDependency(p,s);}
@ -1810,9 +1818,23 @@ namespace test {
+ Duration{nodeExpense_}*(chainLoad_.size()/stressFact_));
}
auto
getInvocationStatistic()
{
return watchInvocations_? watchInvocations_->evaluate()
: lib::IncidenceCount::Statistic{};
}
/* ===== Setter / builders for custom configuration ===== */
ScheduleCtx&&
withInstrumentation (bool doWatch =true)
{
watchInvocations_.reset (doWatch? new lib::IncidenceCount : nullptr);
return move(*this);
}
ScheduleCtx&&
withPlanningStep (microseconds planningTime_per_node)
{

View file

@ -110965,12 +110965,12 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<icon BUILTIN="stop-sign"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1707697757045" ID="ID_1084113416" MODIFIED="1707697943840" TEXT="Instrumentierung im Job &#x27fc; effektive Concurrenvcy">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1707697757045" ID="ID_1084113416" MODIFIED="1707954709271" TEXT="Instrumentierung im Job &#x27fc; effektive Concurrenvcy">
<icon BUILTIN="pencil"/>
<node CREATED="1707753850583" ID="ID_974443691" MODIFIED="1707753886880" TEXT="beide Instrumentierungen kann man hier gemeinsam machen">
<icon BUILTIN="idea"/>
</node>
<node COLOR="#435e98" CREATED="1707753869910" ID="ID_467789923" MODIFIED="1707940141857" TEXT="das ist eine generische Aufgabe: ein IncidenceCount">
<node COLOR="#435e98" CREATED="1707753869910" FOLDED="true" ID="ID_467789923" MODIFIED="1707954699395" TEXT="das ist eine generische Aufgabe: ein IncidenceCount">
<icon BUILTIN="yes"/>
<node CREATED="1707753910239" ID="ID_207246442" MODIFIED="1707754952005" TEXT="Spezifikation">
<icon BUILTIN="info"/>
@ -111115,15 +111115,15 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1707755101530" ID="ID_219913539" MODIFIED="1707755105097" TEXT="Integration">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1707755106784" ID="ID_1004950823" MODIFIED="1707755153862" TEXT="neue Flag schaltbar machen">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1707755101530" ID="ID_219913539" MODIFIED="1707954714696" TEXT="Integration">
<icon BUILTIN="pencil"/>
<node COLOR="#338800" CREATED="1707755106784" ID="ID_1004950823" MODIFIED="1707960767740" TEXT="neue Flag schaltbar machen">
<icon BUILTIN="button_ok"/>
<node CREATED="1707755159128" ID="ID_159603961" MODIFIED="1707755173776" TEXT="kann der smart-ptr selber sein">
<icon BUILTIN="idea"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1707755175332" ID="ID_1338000837" MODIFIED="1707755179358" TEXT="Setter">
<icon BUILTIN="flag-yellow"/>
<node COLOR="#338800" CREATED="1707755175332" ID="ID_1338000837" MODIFIED="1707960769211" TEXT="Setter">
<icon BUILTIN="button_ok"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1707755113190" ID="ID_320712272" MODIFIED="1707755150362" TEXT="IncidenceCount bereitstellen">
@ -111141,8 +111141,58 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<icon BUILTIN="flag-yellow"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1707755248647" ID="ID_1760925660" MODIFIED="1707755268155" TEXT="Einsatz informell verifizieren">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1707755248647" ID="ID_1760925660" MODIFIED="1707960777624" TEXT="Einsatz informell verifizieren">
<icon BUILTIN="pencil"/>
<node BACKGROUND_COLOR="#c8c0b6" CREATED="1707960794133" ID="ID_711451755" MODIFIED="1707960809107" TEXT="neuer Testfall in SchedulerStress_test">
<icon BUILTIN="info"/>
</node>
<node CREATED="1707960810363" ID="ID_1898538089" MODIFIED="1707960828231">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
erster Versuch: <i>&#252;berhaupt keinen </i>Graph konstruieren
</p>
</body>
</html>
</richcontent>
<node COLOR="#435e98" CREATED="1707960829288" ID="ID_1851262882" MODIFIED="1707960963715" TEXT="geht das?">
<node CREATED="1707960839374" ID="ID_1245687450" MODIFIED="1707960846370" TEXT="Bauchgef&#xfc;hl sagt: sollte gehen"/>
<node CREATED="1707960847717" ID="ID_185775784" MODIFIED="1707960867598" TEXT="es ist dann topLevel() &#x2261; 0"/>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1707960869395" ID="ID_1569530368" MODIFIED="1707960879514" TEXT="die Breiten-Bedingung wird verletzt">
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1707960880457" ID="ID_718859944" MODIFIED="1707960887500" TEXT="weil alle Nodes auf Level-0 liegen"/>
<node CREATED="1707960888044" ID="ID_663399582" MODIFIED="1707960895179" TEXT="da es aber keine Verschaltung gibt..."/>
<node CREATED="1707960895735" ID="ID_1170989441" MODIFIED="1707960907090" TEXT="...sollte das kein Problem darstellen"/>
</node>
<node CREATED="1707960908761" ID="ID_1999460077" MODIFIED="1707960967540" TEXT="das Scheduling selber arbeitet auf Level-orientiert &#x2014; und dann gibts halt nur einen">
<icon BUILTIN="idea"/>
</node>
</node>
<node COLOR="#338800" CREATED="1707960970597" ID="ID_1158951100" MODIFIED="1707960975655" TEXT="pragmatisch: l&#xe4;uft">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1707960978559" ID="ID_688220827" MODIFIED="1707960986968" TEXT="Statistik zuschalten">
<icon BUILTIN="forward"/>
<node CREATED="1707960988842" ID="ID_1122286519" MODIFIED="1707960994318" TEXT="erste Ergebnisse sehen gut aus"/>
<node CREATED="1707960994969" ID="ID_835639287" MODIFIED="1707961013083" TEXT="kein Graph &#x27f9; keine Node-weights &#x27f9; lauter leere Jobs"/>
<node CREATED="1707961014519" ID="ID_8171946" MODIFIED="1707961026267" TEXT="ist in &lt; 3ms fertig"/>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1707961027284" ID="ID_995544734" MODIFIED="1707961133751" TEXT="Instrumentierung registiert nur Zeitspanne von 245&#xb5;s">
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1707961224035" ID="ID_47823213" MODIFIED="1707961230985" TEXT="Werte">
<icon BUILTIN="info"/>
<node CREATED="1707961153580" ID="ID_943999161" MODIFIED="1707961171350" TEXT="davon 210&#xb5;s au&#xdf;erhalb jedweder Aktivierung"/>
<node CREATED="1707961179169" ID="ID_1852802808" MODIFIED="1707961198210" TEXT="sowie 34&#xb5;s single-threaded"/>
<node CREATED="1707961198678" ID="ID_1071621043" MODIFIED="1707961218591" TEXT="und 0.5&#xb5;s concurrency &#x2261; 2"/>
</node>
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1707961249356" ID="ID_1961702882" MODIFIED="1707961258458" TEXT="ist das plausibel? kann das sein?">
<icon BUILTIN="help"/>
</node>
</node>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1707755273281" ID="ID_787410697" MODIFIED="1707755280011" TEXT="geeignet Dokumentieren (Test)">
<icon BUILTIN="flag-yellow"/>