Scheduler: complete work-Function / conception work

Notably the work-function is now completely covered, by adding
this last test, and the detailed investigations yesterday
ultimately unveiled nothing of concern; the times sum up.

Further reflection regarding the overall concept led me
to a surprising solution for the problem with priority classes.
This commit is contained in:
Fischlurch 2023-10-27 23:58:40 +02:00
parent e26d251867
commit 552d8dec0e
4 changed files with 294 additions and 47 deletions

View file

@ -244,6 +244,8 @@ namespace gear {
};
/**
* @remark when due, the scheduled Activities are performed within the
* [Activity-Language execution environment](\ref ActivityLang::dispatchChain());

View file

@ -100,6 +100,21 @@ namespace test {
* - this implies we can show timing-delay effects in the millisecond range
* - demonstrated behaviour
* + an Activity already due will be dispatched immediately by post()
* + an Activity due at the point when invoking the work-function is dispatched
* + while queue is empty, the work-function returns immediately, indicating sleep
* + invoking the work-function when there is still some time span up to the next
* planned Activity will enter a targeted sleep, returning shortly after the
* next schedule. Entering then again will cause dispatch of that activity.
* + if the work-function dispatches an Activity while the next entry is planned
* for some time ahead, the work-function will likewise go into a targeted
* sleep and only return at or shortly after that next planned time entry
* + after dispatching an Activity in a situation with no follow-up work,
* the work-function inserts a targeted sleep of random duration,
* to re-shuffle the rhythm of sleep cycles
* + when the next planned Activity has already be »tended for« (by placing
* another worker into a targeted sleep), further workers entering the
* work-function will be re-targeted by a random sleep to focus capacity
* into a time zone behind the next entry.
* @note Invoke the Activity probe itself can take 50..150µs, due to the EventLog,
* which is not meant to be used in performance critical paths but only for tests,
* because it performs lots of heap allocations and string operations. Moreover,
@ -158,6 +173,7 @@ namespace test {
cout << "pullWork() on empty queue..."<<endl;
pullWork(); // Call the work-Function on empty Scheduler queue
CHECK (activity::WAIT == res); // the result instructs this thread to go to sleep immediately
CHECK (delay_us < 40);
cout << "Due at pullWork()..."<<endl;
@ -172,11 +188,11 @@ namespace test {
CHECK (wasInvoked(start));
CHECK (slip_us < 300); // Note: typically there is a slip of 100..200µs, because sleep waits longer
CHECK (scheduler.empty()); // The scheduler is empty now and this thread will go to sleep,
CHECK (delay_us < 20000); // however the sleep-cycle is first re-shuffled by a wait between 0 ... 20ms
CHECK (delay_us < 20200); // however the sleep-cycle is first re-shuffled by a wait between 0 ... 20ms
CHECK (activity::PASS == res); // this thread is instructed to check back once
pullWork();
CHECK (activity::WAIT == res); // ...yet since the queue is still empty, it is sent immediately to sleep
CHECK (delay_us < 20);
CHECK (delay_us < 40);
cout << "next some time ahead => up-front delay"<<endl;
@ -193,12 +209,12 @@ namespace test {
pullWork(); // if we now re-invoke the work-Function as instructed...
CHECK (wasInvoked(start)); // then the next schedule is already slightly overdue and immediately invoked
CHECK (scheduler.empty()); // the queue is empty and thus this thread will be sent to sleep
CHECK (delay_us < 20000); // but beforehand the sleep-cycle is re-shuffled by a wait between 0 ... 20ms
CHECK (delay_us < 20200); // but beforehand the sleep-cycle is re-shuffled by a wait between 0 ... 20ms
CHECK (slip_us < 300);
CHECK (activity::PASS == res); // instruction to check back once
pullWork();
CHECK (activity::WAIT == res); // but next call will send this thread to sleep right away
CHECK (delay_us < 20);
CHECK (delay_us < 40);
cout << "follow-up with some distance => follow-up delay"<<endl;
@ -208,16 +224,8 @@ namespace test {
post (start+t1ms); // But another schedule is placed 1ms behind
sleep_for (100us); // wait for "soon" to pass...
pullWork();
SHOW_EXPR(_raw(now))
SHOW_EXPR(_raw(start))
SHOW_EXPR(_raw(detector.invokeTime(probe)))
SHOW_EXPR(res);
SHOW_EXPR(delay_us)
SHOW_EXPR(slip_us)
SHOW_EXPR(wasInvoked(start))
SHOW_EXPR(scheduler.empty())
CHECK (wasInvoked(start)); // Result: the first invocation happened immediately
CHECK (slip_us < 200);
CHECK (slip_us < 300);
CHECK (delay_us > 900); // yet this thread was afterwards kept in sleep to await the next one
CHECK (activity::PASS == res); // instruction to re-invoke immediately
CHECK (not scheduler.empty()); // since there is still work in the queue
@ -226,13 +234,32 @@ SHOW_EXPR(scheduler.empty())
pullWork(); // re-invoke immediately as instructed
CHECK (wasInvoked(start)); // Result: also the next Activity has been dispatched
CHECK (slip_us < 400); // not much slip
CHECK (slip_us < 20000); // ...and the post-delay is used to re-shuffle the sleep cycle as usual
CHECK (delay_us < 20200); // ...and the post-delay is used to re-shuffle the sleep cycle as usual
CHECK (activity::PASS == res); // since queue is empty, we will call back once...
CHECK (scheduler.empty());
pullWork();
CHECK (activity::WAIT == res); // and then go to sleep.
cout << detector.showLog()<<endl; // HINT: use this for investigation...
cout << "already tended-next => re-target capacity"<<endl;
now = RealClock::now();
start = now + t500us; // Set the next schedule with some distance...
post (start);
// Access scheduler internals (as friend)
CHECK (start == scheduler.layer1_.headTime()); // next schedule indeed appears as next-head
CHECK (not scheduler.loadControl_.tendedNext(start)); // but this next time was not yet marked as "tended"
scheduler.loadControl_.tendNext(start); // manipulate scheduler to mark next-head as "tended"
CHECK ( scheduler.loadControl_.tendedNext(start));
CHECK (start == scheduler.layer1_.headTime()); // other state still the same
CHECK (not scheduler.empty());
pullWork();
CHECK (not wasInvoked(start)); // since next-head was marked as "tended"...
CHECK (not scheduler.empty()); // ...this thread is not used to dispatch it
CHECK (delay_us < 6000); // rather it is re-focussed as free capacity within WORK_HORIZON
}

View file

@ -7041,7 +7041,7 @@ __see also__
&amp;rarr; the protocol [[how to operate the nodes|NodeOperationProtocol]]
</pre>
</div>
<div title="RenderOperationLogic" creator="Ichthyostega" modifier="Ichthyostega" created="202307272322" modified="202309011506" tags="Rendering operational draft" changecount="29">
<div title="RenderOperationLogic" creator="Ichthyostega" modifier="Ichthyostega" created="202307272322" modified="202310272134" tags="Rendering operational draft" changecount="30">
<pre>//The operational logic of Activity execution is the concrete service provided by the [[Scheduler]] to implement interwoven [[render Activities|RenderActivity]] and [[Job execution|RenderJob]].//
* logically, each {{{Activity}}} record represents a //verb// to describe some act performed by »the render process«
* the {{{ActivityLang}}} provides a //builder notation// to build „sentences of activities“ and it sets the framework for //execution// of Activities
@ -7049,7 +7049,7 @@ __see also__
* the ''Scheduler Layer-1'' ({{{SchedulerInvocation}}}) provides the low-level coordination and invocation mechanics to launch [[render Jobs|RenderJob]].
!Framework for Activity execution
The individual {{{Activity}}} records serve as atomic execution elements; an Activity can be invoked once, either by time-bound trigger in the Scheduler's priority queue, or by receiving an activation message (directly when in //management mode,// else indirectly through the invocation queue). The data structure of the {{{Activity}}} record (&amp;rarr; [[description|RenderActivity]]) is maintained by the [[»block flow« memory allocation scheme|SchedulerMemory]] and can be considered stable and available (within the logical limits of its definition, which means until the overarching deadline has passed). The ''activation'' of an Activity causes the invocation of a hard-wired execution logic, taking into account the //type field// of the actual {{{Activity}}} record to be »performed«. This hard-wired logic however can be differentiated into a //generic// part (implemented directly in {{{class Activity}}}) and a //contextual// part, which is indirected through a ''λ-binding'', passed as ''execution context'', yet actually implemented by functions of ''Scheduler Layer-2''.
The individual {{{Activity}}} records serve as atomic execution elements; an Activity shall be invoked once, either by time-bound trigger in the Scheduler's priority queue, or by receiving an activation message (directly when in //management mode,// else indirectly through the invocation queue). The data structure of the {{{Activity}}} record (&amp;rarr; [[description|RenderActivity]]) is maintained by the [[»block flow« memory allocation scheme|SchedulerMemory]] and can be considered stable and available (within the logical limits of its definition, which means until the overarching deadline has passed). The ''activation'' of an Activity causes the invocation of a hard-wired execution logic, taking into account the //type field// of the actual {{{Activity}}} record to be »performed«. This hard-wired logic however can be differentiated into a //generic// part (implemented directly in {{{class Activity}}}) and a //contextual// part, which is indirected through a ''λ-binding'', passed as ''execution context'', yet actually implemented by functions of ''Scheduler Layer-2''.
!!!execution patterns
Since the render engine can be considered performance critical, only a fixed set of //operational patterns// is supported, implemented with a minimum of indirections and thus with limited configurability. It seems indicated to confine the scope of this operational logic to a finite low-level horizon, assuming that //all relevant high-level render activities// can actually be //expressed in terms of these fundamental patterns,// in combination with an opaque JobFunctor.
;Frame Render Job
@ -7201,7 +7201,7 @@ The Scheduler is now considered an implementation-level facility with an interfa
&amp;rarr; [[Workers|SchedulerWorker]]
</pre>
</div>
<div title="SchedulerLoadControl" creator="Ichthyostega" modifier="Ichthyostega" created="202310240240" modified="202310241743" tags="Rendering operational spec draft" changecount="34">
<div title="SchedulerLoadControl" creator="Ichthyostega" modifier="Ichthyostega" created="202310240240" modified="202310272133" tags="Rendering operational spec draft" changecount="48">
<pre>The scheduling mechanism //requires active control of work parameters to achieve good performance on average.//
In a nutshell, the scheduler arranges planned [[render activities|RenderActivity]] onto a time axis -- and is complemented by an [[active »work force«|SchedulerWorker]] to //pull and retrieve// the most urgent next task when free processing capacity becomes available. This arrangement shifts focus from the //management of tasks// towards the //management of capacity// -- which seems more adequate, given that capacity is scarce while tasks are abundant, yet limited in size and processed atomically.
@ -7217,15 +7217,30 @@ Notably the use of special purpose hardware or special OS privileges is //not re
This fundamental strategy translates into the goal to provide each worker with the next piece of work as soon as the thread returns from previous work. Putting this scheme into practice however faces major obstacles stemming from //work granularity// and //timing patterns.// Threads will call for work at essentially random time points, and in no way related to the calculation schedule laid out in advance based on reasoning about dependencies, resource allocation and deadlines. This is a problem hard to overcome on the level of individual operationality. And thus, in accordance to the goals set out above, demanding to aim at optimal use of recurses //on average// -- it is indicated to change to a //statistical perspective// and to interpret the worker call as an //capacity event.//
!!!The statistical flow of capacity
By employing this changed perspective, the ongoing train of worker calls transliterates into a flow of capacity. And this capacity can be grouped and classified. The optimal situation -- which should be achieved on average -- is to be able to provide the next piece of work //immediately// for each worker calling in. On the other hand, target times set out for scheduling should be observed, and thus this optimum state can be characterised as »being slightly overdue«. Ideally, capacity should thus flow-in //slightly behind schedule.// On an operational level, since „capacity flow“ is something generated stochastically, the desired state translates into adjustments to re-distribute the worker callback time points into some focal time zone, located slightly behind the next known [[render activity|RenderActivity]] to tend for. (As aside, an //alternative approach// -- likewise considered for the scheduler design -- would be to pre-attribute known capacity to the next round of known scheduling events; this solution was rejected however, presumably leading to excessively fine-grained and unnecessarily costly computation, disregarding the overall probabilistic nature of the rendering process.)
By employing this changed perspective, the ongoing train of worker calls transliterates into a flow of capacity. And this capacity can be grouped and classified. The optimal situation -- which should be achieved on average -- is to be able to provide the next piece of work //immediately// for each worker calling in. On the other hand, target times set out for scheduling should be observed, and optimum state can thus be characterised as »being slightly overdue«. Ideally, capacity should thus flow-in //slightly behind schedule.// At operational level, since „capacity flow“ is something generated stochastically, the desired state translates into adjustments to re-distribute the worker callback time points into some focal time zone, located slightly behind the next known [[render activity|RenderActivity]] to tend for. (As aside, an //alternative approach// -- likewise considered for the scheduler design -- would be to pre-attribute known capacity to the next round of known scheduling events; this solution was rejected however, presumably leading to excessively fine-grained and unnecessarily costly computation, disregarding the overall probabilistic nature of the rendering process.)
Drawing upon these principles, the treatment of worker calls can be aligned to the upcoming schedule. On average, the planning of this schedule can be assumed to happen in advance with suitably safety margin (notwithstanding the fact that re-scheduling and obliteration of established schedule can happen any time, especially in response to user interaction). The next-known »head element« of the schedule will thus play a //pivotal role,// when placed into the context of the //current time of request.// On average, this head element will be the next activity to consider, and the distance-of-encounter can be taken as indicator for //current density of work.// This allows to establish a priority of concerns to consider when answering a worker pull call, thereby transitioning the capacity-at-hand into a delineated segment of the overall capacity:
Drawing upon these principles, the treatment of worker calls can be aligned to the upcoming schedule. On average, the planning of this schedule can be assumed to happen in advance with suitable safety margin (notwithstanding the fact that re-scheduling and obliteration of established schedule can happen any time, especially in response to user interaction). The next-known »head element« of the schedule will thus play a //pivotal role,// when placed into the context of the //current time of request.// On average, this head element will be the next activity to consider, and the distance-of-encounter can be taken as indicator for //current density of work.// This allows to establish a priority of concerns to consider when answering a worker pull call, thereby transitioning the capacity-at-hand into a dedicated segment of the overall capacity:
# ideally, activities slightly overdue can be satisfied by //active capacity//
# spinning-wait is adequate for imminent events below scheduling latency
# next priority to consider is a head element located more into the future
# beyond that, capacity can be redirected into a focussed zone behind the head
# and if the head is far away, capacity is transitioned into the reserve segment
Worker capacity events can be further distinguished into //incoming capacity// and //outgoing capacity.// The latter is the most desirable transition, since it corresponds to a worker just returning from a computation task, while incoming capacity stems from workers calling back after a sleep period. Thus a high priority is placed on re-assigning the outgoing capacity immediately back to further active work, while for incoming capacity there is an increased preference to send it back to sleep. A worker placed into the sleeping reserve for an extended stretch of time can be considered excess capacity and will be removed from the [[work force|SchedulerWorker]]. This kind of asymmetry creates a cascading flow, and allows in the end to synthesise an average load factor, which in turn can be used to regulate the Engine on a global level. If scheduling is overloaded, increasing the slip of planned timings, more capacity might be added back when available, or the planning process should be throttled accordingly.
Worker capacity events can be further distinguished into //incoming capacity// and //outgoing capacity.// The latter is the most desirable transition, since it corresponds to a worker just returning from a computation task, while incoming capacity stems from workers calling back after a sleep period. Thus a high priority is placed on re-assigning the outgoing capacity immediately back to further active work, while for incoming capacity there is a preference to send it back to sleep. A worker placed into the sleeping reserve for an extended stretch of time can be considered excess capacity and will be removed from the [[work force|SchedulerWorker]]. This kind of asymmetry creates a cascading flow, and allows in the end to synthesise an average load factor, which in turn can be used to regulate the Engine on a global level. If scheduling is overloaded, increasing the slip of planned timings, more capacity might be added back when available, or the planning process should be throttled accordingly.
!!!Playback and processing priority
Not all tasks are on equal footing though. Given the diverse nature of functions fulfilled by the render engine, several kinds of render processes can be distinguished:
;Free-wheeling
:Final quality rendering must be completed flawlessly, //whatever it takes.//
:Yet there is no temporal limit, rather it is tantamount to use resources efficiently.
;Time-bound
:Media playback has to observe clear temporal constraints and is obsolete when missing deadlines.
:Making progress when possible is more important than completing every chain of activities
;Background
:Various service tasks can be processed by the render engine when there is excess capacity to use.
For the likes of UI audio visualisation or preview caching there is typically no deadline at all, as long as they do not jeopardise important work
These distinct »processing classes« seem to impose almost contradictory calculation patterns and goals, hard to fit into a single organisational scheme. Yet the //statistical view angle// outlined above offers a way to reconcile such conflicting trends -- assuming that there is a pre-selection on a higher organisational level to ensure the overall processing load can be handled with the given resources. Indeed it is pointless even to start three real-time playback processes in a situation where it is blatantly clear that the system can handle only two. Yet when a rough guess of expected load indicates that a real-time playback will only consume half of the system's processing capacity, even overprovisioning of background processes to some degree is feasible, as long as the real-time playback gets the necessary headroom. And all these different processing patterns can be handled within the same scheduling framework, as long as they can be translated into a //probability to acquire the resources.//
A task placed into the scheduling timeline basically represents some //»cross-section of probability«.// If work capacity happens to „land“ into its definition realm and no further overdue task is scheduled before, the task can use the available capacity. Yet when the deadline of the task has passed without any free capacity „hitting“ its scope, its chances are wasted and the scheduler will discard the entry. And by exploiting these relationships, it is possible to craft a scheme of time slot allocation to translate an abstract processing priority into a probabilistic access to some fraction of capacity. Even more so, since it is possible to place multiple instances of the same task into the scheduling timeline, relying on the [[»Gate« mechanism|RenderOperationLogic]] of the [[Activity-Language|RenderActivity]], to spread out a small level of background processing probability over an extended period of time.
</pre>
</div>
<div title="SchedulerMemory" creator="Ichthyostega" modifier="Ichthyostega" created="202307031622" modified="202310241427" tags="Rendering operational draft" changecount="31">

View file

@ -82420,8 +82420,8 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
</node>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1697663918107" ID="ID_210862409" MODIFIED="1697675123151" TEXT="Load-Controller vorsehen">
<linktarget COLOR="#75769b" DESTINATION="ID_210862409" ENDARROW="Default" ENDINCLINATION="-667;72;" ID="Arrow_ID_1800712901" SOURCE="ID_464175339" STARTARROW="None" STARTINCLINATION="-540;37;"/>
<linktarget COLOR="#af587f" DESTINATION="ID_210862409" ENDARROW="Default" ENDINCLINATION="-33;645;" ID="Arrow_ID_204955453" SOURCE="ID_1110932778" STARTARROW="None" STARTINCLINATION="250;17;"/>
<linktarget COLOR="#75769b" DESTINATION="ID_210862409" ENDARROW="Default" ENDINCLINATION="-667;72;" ID="Arrow_ID_1800712901" SOURCE="ID_464175339" STARTARROW="None" STARTINCLINATION="-540;37;"/>
<icon BUILTIN="flag-pink"/>
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1697808322602" ID="ID_1787549088" MODIFIED="1697808340997" TEXT="Entwickelt sich wohl in Richtung einer Steuerzentrale">
<icon BUILTIN="messagebox_warning"/>
@ -82722,9 +82722,10 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<node CREATED="1697941881102" ID="ID_1281507935" MODIFIED="1697941890218" TEXT="else: switch to sleep"/>
</node>
</node>
<node CREATED="1698020504097" ID="ID_1156275715" MODIFIED="1698020521449" TEXT="Bedeutung der Zeit-Ordnung">
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#420117" CREATED="1698020504097" ID="ID_1156275715" MODIFIED="1698432446373" TEXT="Bedeutung der Zeit-Ordnung">
<icon BUILTIN="idea"/>
<node CREATED="1698020524734" ID="ID_1985773186" MODIFIED="1698020550679" TEXT="hier hilft eine probabilistische Betrachtung &#x27f6; Flow">
<node BACKGROUND_COLOR="#eada9f" COLOR="#990000" CREATED="1698020524734" ID="ID_1985773186" MODIFIED="1698432667093" TEXT="hier hilft eine probabilistische Betrachtung &#x27f6; Flow">
<arrowlink COLOR="#722322" DESTINATION="ID_1475863913" ENDARROW="Default" ENDINCLINATION="888;-37;" ID="Arrow_ID_1254135767" STARTARROW="None" STARTINCLINATION="741;41;"/>
<icon BUILTIN="idea"/>
</node>
<node CREATED="1698020552307" ID="ID_124426025" MODIFIED="1698020593813" TEXT="Worker melden sich regelm&#xe4;&#xdf;ig &#x27f6; Kapazit&#xe4;t flie&#xdf;t in bestimmte Kategorien"/>
@ -83169,8 +83170,8 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1698159441179" ID="ID_486995126" MODIFIED="1698372610717" TEXT="Scheduler-Tick">
<linktarget COLOR="#994d72" DESTINATION="ID_486995126" ENDARROW="Default" ENDINCLINATION="-91;14;" ID="Arrow_ID_973238030" SOURCE="ID_1391329454" STARTARROW="None" STARTINCLINATION="71;5;"/>
<linktarget COLOR="#fffcc5" DESTINATION="ID_486995126" ENDARROW="Default" ENDINCLINATION="579;-95;" ID="Arrow_ID_1801107947" SOURCE="ID_671338782" STARTARROW="None" STARTINCLINATION="292;11;"/>
<linktarget COLOR="#994d72" DESTINATION="ID_486995126" ENDARROW="Default" ENDINCLINATION="-91;14;" ID="Arrow_ID_973238030" SOURCE="ID_1391329454" STARTARROW="None" STARTINCLINATION="71;5;"/>
<icon BUILTIN="flag-yellow"/>
</node>
</node>
@ -88169,11 +88170,12 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<arrowlink COLOR="#ff1924" DESTINATION="ID_996573918" ENDARROW="Default" ENDINCLINATION="-817;60;" ID="Arrow_ID_226641198" STARTARROW="None" STARTINCLINATION="-848;-40;"/>
<icon BUILTIN="hourglass"/>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1697484506891" ID="ID_1292738036" MODIFIED="1698247370951" TEXT="den kompletten Work-Funktor durchspielen">
<icon BUILTIN="pencil"/>
<node COLOR="#338800" CREATED="1697484506891" ID="ID_1292738036" MODIFIED="1698448216625" TEXT="den kompletten Work-Funktor durchspielen">
<icon BUILTIN="button_ok"/>
<node CREATED="1698243387222" ID="ID_1151484757" MODIFIED="1698243397453" TEXT="und zwar direkten Aufruf, ohne WorkForce"/>
<node CREATED="1698243399946" ID="ID_525069730" MODIFIED="1698243412341" TEXT="es kann zu Sleeps kommen"/>
<node CREATED="1698243426549" ID="ID_1975979569" MODIFIED="1698243431240" TEXT="das bedeutet: Zeitmessung">
<node CREATED="1698243426549" ID="ID_1975979569" MODIFIED="1698448212804" TEXT="das bedeutet: Zeitmessung">
<icon BUILTIN="clock"/>
<node CREATED="1698269582160" ID="ID_211702790" MODIFIED="1698269611010" TEXT="und Pr&#xfc;fen unscharfer Zeitabst&#xe4;nde">
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1698269686974" ID="ID_586911111" MODIFIED="1698269697257" TEXT="wie viel Genauigkeit kann man da &#xfc;berhaupt erwarten?"/>
@ -88187,35 +88189,37 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</body>
</html></richcontent>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1698269741040" ID="ID_868809913" MODIFIED="1698269752607" TEXT="verusche mal mein Gl&#xfc;ck mit 500&#xb5;s">
<icon BUILTIN="pencil"/>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1698269741040" ID="ID_868809913" MODIFIED="1698448202804" TEXT="verusche mal mein Gl&#xfc;ck mit ~ 500&#xb5;s">
<icon BUILTIN="bell"/>
</node>
</node>
<node CREATED="1698269618101" ID="ID_561925985" MODIFIED="1698269648550" TEXT="...das macht diesen Test ziemlich fragw&#xfc;rdig">
<icon BUILTIN="broken-line"/>
</node>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1698269656739" ID="ID_851832685" MODIFIED="1698269684671" TEXT="mu&#xdf; auf sehr deutliche Zeitungerschiede abzielen">
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1698269656739" ID="ID_851832685" MODIFIED="1698448182187" TEXT="mu&#xdf; auf sehr deutliche Zeitungerschiede abzielen">
<icon BUILTIN="yes"/>
</node>
</node>
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1698243664309" ID="ID_654541946" MODIFIED="1698243687277" TEXT="was kann man sinnvollerweise pr&#xfc;fen?">
<node COLOR="#435e98" CREATED="1698243664309" ID="ID_654541946" MODIFIED="1698448102750" TEXT="was kann man sinnvollerweise pr&#xfc;fen?">
<icon BUILTIN="help"/>
<node COLOR="#435e98" CREATED="1698243697817" ID="ID_83311166" MODIFIED="1698282740280" TEXT="eine eingespiele Activity wird aufgerufen"/>
<node COLOR="#435e98" CREATED="1698243756225" ID="ID_489965107" MODIFIED="1698282742903" TEXT="was f&#xe4;llig ist, wird direkt vom post() aufgerufen"/>
<node COLOR="#435e98" CREATED="1698243756225" ID="ID_831882516" MODIFIED="1698282748223" TEXT="was f&#xe4;llig wurde, wird unverz&#xf6;gert aufgerufen"/>
<node CREATED="1698243881573" ID="ID_1520192624" MODIFIED="1698243892539" TEXT="es wird genau eine scheduled Activity aufgerufen"/>
<node CREATED="1698243927930" ID="ID_1326757783" MODIFIED="1698243935709" TEXT="die Zeit wird in etwa eingehalten"/>
<node CREATED="1698243794737" ID="ID_1290805695" MODIFIED="1698243800463" TEXT="das Delay-Regime">
<node COLOR="#435e98" CREATED="1698243927930" ID="ID_1326757783" MODIFIED="1698445119916" TEXT="die Zeit wird in etwa eingehalten"/>
<node COLOR="#435e98" CREATED="1698243794737" ID="ID_1290805695" MODIFIED="1698448111059" TEXT="das Delay-Regime">
<icon BUILTIN="forward"/>
<node COLOR="#338800" CREATED="1698243828655" ID="ID_763249245" MODIFIED="1698377479489" TEXT="Abstand davor &#x27f6; delay">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1698243897342" ID="ID_1722957398" MODIFIED="1698377481239" TEXT="Abstand danch &#x27f6; delay">
<icon BUILTIN="button_ok"/>
</node>
<node CREATED="1698243973380" ID="ID_664100190" MODIFIED="1698244000012" TEXT="tendedNext &#x27f6; re-Fokussiert"/>
<node COLOR="#338800" CREATED="1698245002134" ID="ID_1016395299" MODIFIED="1698377444088" TEXT="nichts danach &#x27f6; re-Distribution">
<node COLOR="#338800" CREATED="1698243973380" ID="ID_664100190" MODIFIED="1698448098679" TEXT="tendedNext &#x27f6; re-Fokussiert">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1698245002134" ID="ID_1016395299" MODIFIED="1698448116126" TEXT="nichts danach &#x27f6; re-Distribution">
<linktarget COLOR="#758fa6" DESTINATION="ID_1016395299" ENDARROW="Default" ENDINCLINATION="302;-22;" ID="Arrow_ID_1135387860" SOURCE="ID_28419292" STARTARROW="None" STARTINCLINATION="246;369;"/>
<linktarget COLOR="#76a2bf" DESTINATION="ID_1016395299" ENDARROW="Default" ENDINCLINATION="334;25;" ID="Arrow_ID_318616986" SOURCE="ID_506319711" STARTARROW="None" STARTINCLINATION="186;-10;"/>
<linktarget COLOR="#76a2bf" DESTINATION="ID_1016395299" ENDARROW="Default" ENDINCLINATION="334;27;" ID="Arrow_ID_318616986" SOURCE="ID_506319711" STARTARROW="None" STARTINCLINATION="186;-10;"/>
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1698245073987" ID="ID_1953896637" MODIFIED="1698377472196" TEXT="leer &#x27f6; Schlaf">
@ -88263,7 +88267,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</body>
</html></richcontent>
</node>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1698285072189" ID="ID_1552477309" MODIFIED="1698377307677" TEXT="Logik-Fehler mit tendNext">
<node COLOR="#435e98" CREATED="1698285072189" FOLDED="true" ID="ID_1552477309" MODIFIED="1698448138291" TEXT="Logik-Fehler mit tendNext">
<icon BUILTIN="broken-line"/>
<node CREATED="1698285085530" ID="ID_991445216" MODIFIED="1698285104841">
<richcontent TYPE="NODE"><html>
@ -88279,7 +88283,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<icon BUILTIN="ksmiletris"/>
</node>
<node COLOR="#435e98" CREATED="1698285158209" ID="ID_506319711" MODIFIED="1698377451936" TEXT="korrekt w&#xe4;re: per Nach-Verz&#xf6;gerung die Schl&#xe4;fer umverteilen">
<arrowlink COLOR="#76a2bf" DESTINATION="ID_1016395299" ENDARROW="Default" ENDINCLINATION="334;25;" ID="Arrow_ID_318616986" STARTARROW="None" STARTINCLINATION="186;-10;"/>
<arrowlink COLOR="#76a2bf" DESTINATION="ID_1016395299" ENDARROW="Default" ENDINCLINATION="334;27;" ID="Arrow_ID_318616986" STARTARROW="None" STARTINCLINATION="186;-10;"/>
<icon BUILTIN="forward"/>
<node COLOR="#338800" CREATED="1698377318108" ID="ID_362432031" MODIFIED="1698377343794" TEXT="tendedNext mu&#xdf; explizit die Rand-Zeiten korrekt ausschlie&#xdf;en">
<icon BUILTIN="button_ok"/>
@ -88568,8 +88572,14 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
</node>
</node>
<node COLOR="#338800" CREATED="1698448145932" ID="ID_1467908821" MODIFIED="1698448171087" TEXT="abgesehen davon: alle Timings sind jetzt plausibel">
<icon BUILTIN="bookmark"/>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1698448289137" ID="ID_737541011" MODIFIED="1698448319081" TEXT="einen Render-Job (high-level) schedulen">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1697473158929" ID="ID_1723058065" MODIFIED="1697484548083" TEXT="vollst&#xe4;ndiger Ende-zu-Ende Test incl. WorkForce">
<icon BUILTIN="flag-yellow"/>
</node>
@ -88594,14 +88604,15 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
</node>
<node CREATED="1697758391611" ID="ID_124711923" MODIFIED="1697758397083" TEXT="F&#xe4;lle">
<node CREATED="1697758425500" ID="ID_1450418166" MODIFIED="1697758430911" TEXT="simpleUsage">
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1697758425500" ID="ID_1450418166" MODIFIED="1698448262765" TEXT="simpleUsage">
<icon BUILTIN="hourglass"/>
<node CREATED="1697758440737" ID="ID_1211803424" MODIFIED="1697758454084" TEXT="zeigt hier vor allem was man braucht"/>
<node COLOR="#338800" CREATED="1697842432420" ID="ID_1052962927" MODIFIED="1697842447753" TEXT="Scheduler l&#xe4;&#xdf;t sich instantiieren">
<icon BUILTIN="button_ok"/>
</node>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1698247388556" ID="ID_1402046356" MODIFIED="1698247391396" TEXT="invokeWorkFunction">
<icon BUILTIN="pencil"/>
<node COLOR="#338800" CREATED="1698247388556" ID="ID_1402046356" MODIFIED="1698448243571" TEXT="invokeWorkFunction">
<icon BUILTIN="button_ok"/>
<node COLOR="#338800" CREATED="1698247394051" ID="ID_198829622" MODIFIED="1698247420009" TEXT="kann einfachen Aufruf einer vorher eingespielten Activity belegen">
<icon BUILTIN="button_ok"/>
</node>
@ -88621,6 +88632,9 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<node COLOR="#338800" CREATED="1698284614614" ID="ID_98846661" MODIFIED="1698284618170" TEXT="delay danach">
<icon BUILTIN="button_ok"/>
</node>
<node COLOR="#338800" CREATED="1698448228557" ID="ID_238462097" MODIFIED="1698448239965" TEXT="re-Fokussieren der Kapazit&#xe4;t">
<icon BUILTIN="button_ok"/>
</node>
</node>
</node>
</node>
@ -91744,7 +91758,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
</node>
<node CREATED="1685316762484" ID="ID_779629114" MODIFIED="1685316844661" TEXT="wir nutzen Zeitsteuerung als integrierendes Agens" VSHIFT="18">
<arrowlink COLOR="#f9ffd5" DESTINATION="ID_503599380" ENDARROW="Default" ENDINCLINATION="-127;-352;" ID="Arrow_ID_215108045" STARTARROW="None" STARTINCLINATION="-65;91;"/>
<arrowlink COLOR="#f9ffd5" DESTINATION="ID_503599380" ENDARROW="Default" ENDINCLINATION="-127;-352;" ID="Arrow_ID_215108045" STARTARROW="None" STARTINCLINATION="-107;177;"/>
<linktarget COLOR="#ffeac4" DESTINATION="ID_779629114" ENDARROW="Default" ENDINCLINATION="13;-20;" ID="Arrow_ID_1083704754" SOURCE="ID_1469791" STARTARROW="None" STARTINCLINATION="-69;0;"/>
<icon BUILTIN="idea"/>
<node CREATED="1685317008821" ID="ID_75624396" MODIFIED="1685317551995" TEXT="den zus&#xe4;tzlichen Overhead m&#xfc;ssen wir durch Parallelisierungs-Gewinn aufwiegen">
@ -91815,10 +91829,10 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</body>
</html></richcontent>
</node>
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1687652356899" ID="ID_1155443486" MODIFIED="1687652375875" TEXT="K&#xf6;nnen Priorit&#xe4;ts-Klassen &#xfc;berhaupt dargestellt werden?">
<node COLOR="#435e98" CREATED="1687652356899" ID="ID_1155443486" MODIFIED="1698433460035" TEXT="K&#xf6;nnen Priorit&#xe4;ts-Klassen &#xfc;berhaupt dargestellt werden?">
<icon BUILTIN="help"/>
<node CREATED="1687652377638" ID="ID_927055665" MODIFIED="1687652408889" TEXT="z.B. Realtime vs. Background"/>
<node CREATED="1687652466615" ID="ID_98119896" MODIFIED="1687652589634" TEXT="der Background-Task k&#xf6;nnte eine fr&#xfc;here Deadline haben....">
<node CREATED="1687652466615" ID="ID_98119896" MODIFIED="1698432345620" TEXT="Problem: der Background-Task k&#xf6;nnte eine fr&#xfc;here Deadline haben....">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
@ -91829,6 +91843,195 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</html></richcontent>
<icon BUILTIN="messagebox_warning"/>
</node>
<node BACKGROUND_COLOR="#e8e78a" COLOR="#a34037" CREATED="1698431962633" ID="ID_1475863913" MODIFIED="1698433650097" TEXT="Heureka! mit dem probabilstischen Ansatz verbinden">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
Gru&#223; vom Vollmond in Pullach (noch nicht ganz voll, morgen ist Mondfinsternis), der geht das Tal entlang mit
</p>
</body>
</html>
</richcontent>
<arrowlink COLOR="#f5fd9d" DESTINATION="ID_323819142" ENDARROW="Default" ENDINCLINATION="44;-68;" ID="Arrow_ID_116183984" STARTARROW="None" STARTINCLINATION="-157;7;"/>
<linktarget COLOR="#722322" DESTINATION="ID_1475863913" ENDARROW="Default" ENDINCLINATION="888;-37;" ID="Arrow_ID_1254135767" SOURCE="ID_1985773186" STARTARROW="None" STARTINCLINATION="741;41;"/>
<font BOLD="true" NAME="SansSerif" SIZE="12"/>
<icon BUILTIN="idea"/>
<node CREATED="1698433179997" HGAP="46" ID="ID_477130123" MODIFIED="1698433421017" TEXT="Kapazit&#xe4;ts- und Laststeuerung geh&#xf6;rt eigentlich auf eine h&#xf6;here Ebene" VSHIFT="41">
<icon BUILTIN="full-1"/>
</node>
<node CREATED="1698433228231" HGAP="32" ID="ID_1931146744" MODIFIED="1698433423425" TEXT="denn wenn ein Task mal im Scheuler ist &#x2014; hat er Chancen, Kapazit&#xe4;t zu erlangen" VSHIFT="-1">
<icon BUILTIN="full-2"/>
</node>
<node CREATED="1698433311382" HGAP="24" ID="ID_788626614" MODIFIED="1698433453837" TEXT="allerdings erforder Kapazit&#xe4;ts-Steuerung eine Priorisierung &#x2014; und zwar segmentweise">
<icon BUILTIN="full-3"/>
</node>
<node CREATED="1698433349337" ID="ID_773792225" MODIFIED="1698433434845" TEXT="also kann man mit Tasks auch segmentweise &#x201e;schraffieren&#x201c;">
<icon BUILTIN="full-4"/>
</node>
<node CREATED="1698433382797" ID="ID_856572541" MODIFIED="1698433400374" TEXT="&#xd83e;&#xdc32; das ist die &#xd83d;&#xdca1; L&#xf6;sung"/>
</node>
</node>
<node CREATED="1698433489965" ID="ID_323819142" MODIFIED="1698433626101">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
<font size="4"><u>Priorit&#228;t</u></font>&#160;bestimmt die <b>Wahrscheinlichkeit</b>,
</p>
<p>
Kapazit&#228;t zuf&#228;llig zu erlangen
</p>
</body>
</html>
</richcontent>
<linktarget COLOR="#f5fd9d" DESTINATION="ID_323819142" ENDARROW="Default" ENDINCLINATION="44;-68;" ID="Arrow_ID_116183984" SOURCE="ID_1475863913" STARTARROW="None" STARTINCLINATION="-157;7;"/>
<icon BUILTIN="forward"/>
<node CREATED="1698433687865" HGAP="17" ID="ID_1856291258" MODIFIED="1698436739705" TEXT="ein Render-Proze&#xdf; setzt Kapazit&#xe4;t voraus" VSHIFT="16">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
Grunds&#228;tzlich darf ein Render-Proze&#223; nur bei vorhandener Kapazit&#228;t zugelassen werden. Aber die Anwendung dieses Prinzips hat Abstufungen, die sich aus der Art des Prozesses bestimmen.
</p>
<ul>
<li>
ein <i>whatever it takes</i>&#160;- Render bekommt die gesamte Kapazit&#228;t. Punkt. (alles andere w&#252;rde ihm schaden)
</li>
<li>
ein realtime-Playback mu&#223; im Stande sein, seine Termine zu halten; unter dieser Ma&#223;gabe mu&#223; er Vorfahrt bekommen, kann aber durchaus noch Raum freilassen, wenn er nicht au&#223;erdem auch noch die Leistungsf&#228;higkeit des Systems komplett aussch&#246;pft
</li>
<li>
demgegen&#252;ber kann man Background-Prozesse dem Wettbewerb &#252;berlassen, und dabei sogar noch verschiedene Level definieren. Es sind nur zwei Bedingungen einzuhalten:
</li>
<ul>
<li>
Activities m&#252;ssen feingranular sein, damit sie wirklich nur die Reste verbrauchen
</li>
<li>
es mu&#223; daf&#252;r gesorgt werden, da&#223; der Proze&#223; tats&#228;chlich fortschreitet
</li>
</ul>
</ul>
</body>
</html>
</richcontent>
</node>
<node CREATED="1698434259630" ID="ID_1030217927" MODIFIED="1698434360871" TEXT="ein geSchedulter Task hat einen Wahrscheinlichkeitsquerschnitt">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
die aktuelle Situation bestimmt dar&#252;ber, wie Kapazit&#228;t zugef&#252;hrt wird; aber der Querschnitt bestimmt, wie gute Chancen ein konkreter Task hat, davon etwas abzubekommen
</p>
</body>
</html>
</richcontent>
<node CREATED="1698434362407" ID="ID_1045531877" MODIFIED="1698434532770" TEXT="solange etwas davor steht, ist er verdeckt">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
Ein <i>frei stehender</i>&#160;Task bekommt durch den tend-Next-Mechanismus auch schon sehr viel fr&#252;her die n&#228;chste frei werdende Kapazit&#228;t zugeordnet; solange aber in der einfachen zeitlichen Ordnung noch etwas vor ihm steht (selbst wenn &#252;berf&#228;llig), dann zieht dieses die Priorit&#228;t auf sich
</p>
</body>
</html>
</richcontent>
</node>
<node CREATED="1698434413940" ID="ID_1412965140" MODIFIED="1698434678138">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
sobald er aber frei steht, bestimmt seine <b>L&#228;nge</b>&#160;die Priorit&#228;t
</p>
</body>
</html>
</richcontent>
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
Und zwar durch das Ende (die Deadline), nach deren &#220;berstreichen der Task effektiv unwirksam ist und im Vorr&#252;bergehen entnommen und verworfen wird. Wenn nun verschiedene Tasks jeweils in der L&#228;nge beschr&#228;nkt sind, dann f&#228;llt ihnen diejenige Kapazit&#228;t zu, die zuf&#228;llig in ihrem Wirkradius <i>&#8222;landet&#8220;</i>
</p>
</body>
</html>
</richcontent>
</node>
<node CREATED="1698434700812" ID="ID_1790239617" MODIFIED="1698434978201" TEXT="weitere Faktoren modulieren dieses Schema">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
Da sind zun&#228;chst die Gates von Relevanz. Ein noch geschlossenes Gate kann einen Task nach hinten schieben und damit andere Tasks aufdecken. Ein getriggertes und endg&#252;ltig geschlossenes Gate nimmt den Task aus der Konkurrenz komplett heraus. Und au&#223;erdem werden Tasks auch noch &#252;ber eine Proze&#223;/Kontext-ID gekennzeichnet, wodurch eine Revision und Aktualisierung eines gesamten Planungsvorgangs m&#246;glich wird.
</p>
</body>
</html>
</richcontent>
</node>
</node>
<node CREATED="1698434985257" ID="ID_1456084825" MODIFIED="1698435345311" TEXT="man kann die gleiche Chain mehrfach schedulen &#x2014; sofern sie ein Gate-Enablement hat">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
...das bedeutet, wenn wir einmal durch das Gate gegangen sind, ist es endg&#252;ltig geschlossen. Deshalb k&#246;nnen gewisserma&#223;en mehrere &#187;Instanzen&#171; eingeplant werden, denn das Wegr&#228;umen von M&#252;ll ist in einer Priority-Queue relativ effizient, O(log&#8345;), und l&#228;uft bei uns im einstelligen &#181;s-Bereich pro Einzelschritt
</p>
</body>
</html>
</richcontent>
</node>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#411d16" CREATED="1698435413188" HGAP="18" ID="ID_988639193" MODIFIED="1698436797466" STYLE="bubble" TEXT="&#x27f9; Wir definieren ein Schema, nach dem Tasks in Zeitfenster gesetzt werden" VSHIFT="4">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
Ein Task der nur Restkapazit&#228;t bekommen soll, darf niemals am Anfang des Fensters stehen, und auch m&#246;glichst nicht ganz am Ende. Je k&#252;rzer und je mehr in der Mitte, desto geringer seine Chancen. Ein Task der in jedem Fall Kapazit&#228;t bekommen soll, mu&#223; nur hinreichend weit nach hinten reichen (aber unter der Einschr&#228;nkung, unseren Epochen-basierten BlockFlow nicht zu &#252;berlasten). Oder er mu&#223; hinreichend oft wiederholt werden. Es wird also ein generelles Segment-Schema etabliert, und in diesem gibt es vorgefertigte &#187;Slots&#171;. Gem&#228;&#223; &#252;bergeordnete Kapazit&#228;tsplanung werden diese Slots in Anspruch genommen. Wenn wir beispielsweise eine Restkapazit&#228;t 10-fach &#252;berprovisionieren, dann bedeutet das, jeden einzelnen Task (ggfs. mit Zeitabstand) 10+x-mal einzuplanen, wobei x einen empirisch bestimmten safety-margin darstellt, um die tats&#228;chlichen Fluktuationen der Kapazit&#228;t abzufedern...
</p>
</body>
</html>
</richcontent>
</node>
<node CREATED="1698436618074" HGAP="23" ID="ID_145123835" MODIFIED="1698436752191" VSHIFT="5">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p style="text-align: center">
dadurch &#252;bersetzen wir multidimensionale Zusammenh&#228;nge
</p>
<p style="text-align: center">
von der &#252;bergeordneten Planungsebene
</p>
<p style="text-align: center">
in ein low-Level-Ausf&#252;hrungsschema
</p>
</body>
</html>
</richcontent>
<icon BUILTIN="yes"/>
</node>
</node>
</node>
<node CREATED="1698097270027" ID="ID_1129105536" MODIFIED="1698097290884" TEXT="Timing-Genauigkeit">
@ -91866,7 +92069,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
</node>
<node CREATED="1685320663808" ID="ID_503599380" MODIFIED="1685320675723" TEXT="Struktur-Elemente">
<linktarget COLOR="#f9ffd5" DESTINATION="ID_503599380" ENDARROW="Default" ENDINCLINATION="-127;-352;" ID="Arrow_ID_215108045" SOURCE="ID_779629114" STARTARROW="None" STARTINCLINATION="-65;91;"/>
<linktarget COLOR="#f9ffd5" DESTINATION="ID_503599380" ENDARROW="Default" ENDINCLINATION="-127;-352;" ID="Arrow_ID_215108045" SOURCE="ID_779629114" STARTARROW="None" STARTINCLINATION="-107;177;"/>
<node CREATED="1685320702161" ID="ID_1864388669" MODIFIED="1685320717483">
<richcontent TYPE="NODE"><html>
<head/>