diff --git a/src/vault/gear/scheduler.hpp b/src/vault/gear/scheduler.hpp index 1a47665df..41e642383 100644 --- a/src/vault/gear/scheduler.hpp +++ b/src/vault/gear/scheduler.hpp @@ -79,6 +79,13 @@ ** Since the scheduler queue only stores references to render activities, which are ** allocated in a [special arrangement](\ref BlockFlow) exploiting the known deadline ** time of each task, further processing can commence concurrently. + ** @note The grooming-token should always be dropped by a deliberate state transition. + ** Notably _internal processing_ (e.g. planning of new jobs) will _not_ drop + ** the token, since it must be able to change the schedule. Such internal + ** tasks can be processed in row and will be confined to a single thread + ** (there is a special treatment at the end of #getWork() to achieve that). + ** As a safety net, the grooming-token will automatically be dropped after + ** catching an exception, or when a thread is sent to sleep. ** ** @see SchedulerService_test Component integration test ** @see SchedulerStress_test @@ -108,7 +115,6 @@ //#include "lib/symbol.hpp" #include "lib/nocopy.hpp" //#include "lib/util.hpp" -#include "lib/format-cout.hpp"/////////////////TODO //#include #include @@ -264,7 +270,7 @@ namespace gear { /** send this thread into a targeted short-time wait. */ - activity::Proc scatteredDelay (Time now, LoadController::Capacity,bool in);////////////TODO + activity::Proc scatteredDelay (Time now, LoadController::Capacity); /** @@ -411,7 +417,7 @@ namespace gear { Time now = ctx.getSchedTime(); Time head = layer1_.headTime(); return scatteredDelay(now, - loadControl_.markIncomingCapacity (head,now),true); + loadControl_.markIncomingCapacity (head,now)); }) .performStep([&]{ Time now = ctx.getSchedTime(); @@ -422,7 +428,7 @@ namespace gear { Time now = ctx.getSchedTime(); Time head = layer1_.headTime(); return scatteredDelay(now, - loadControl_.markOutgoingCapacity (head,now),false); + loadControl_.markOutgoingCapacity (head,now)); }); // ensure lock clean-up @@ -454,7 +460,7 @@ namespace gear { * place the current thread into a short-term targeted sleep. */ inline activity::Proc - Scheduler::scatteredDelay (Time now, LoadController::Capacity capacity, bool in) + Scheduler::scatteredDelay (Time now, LoadController::Capacity capacity) { auto doTargetedSleep = [&] { // ensure not to block the Scheduler after management work @@ -463,11 +469,7 @@ namespace gear { layer2_.dropGroomingToken(); // relocate this thread(capacity) to a time where its more useful Offset targetedDelay = loadControl_.scatteredDelayTime (now, capacity); -TimeVar head = layer1_.headTime()-now; -cout <<"\n|oo|"<<(in?"^":"v")<<" Sleep-->"<<_raw(targetedDelay)<<" 1.0) + { + ++phase; + peak1_s = offset(); + } + break; + case 1: + peak1_max = max (load, peak1_max); + if (load < 1.0) + { + ++phase; + peak1_dur = offset() - peak1_s; + } + break; + case 2: + if (load > 1.0) + { + ++phase; + peak2_s = offset(); + } + break; + case 3: + peak2_max = max (load, peak2_max); + if (load < 1.0) + { + ++phase; + peak2_dur = offset() - peak2_s; + } + break; + } + cout << row % offset() % load + % offset(scheduler.layer1_.headTime()) + % scheduler.loadControl_.averageLag(); } + uint done = offset(); + + //--------Summary-Table------------------------------ + _Fmt peak{"\nPeak %d ....... %5d +%dµs %34tmax=%3.1f"}; + cout << "-------+-------------+----------+----------" + << "\n\n" + << peak % 1 % peak1_s % peak1_dur % peak1_max + << peak % 2 % peak2_s % peak2_dur % peak2_max + << "\nTick ....... "< 5000); // first peak was scheduled at 5ms + CHECK (peak1_s < 10000); + CHECK (peak2_s > 15000); // second peak was scheduled at 15ms + CHECK (peak2_s < 20000); + CHECK (peak1_max > 2.0); + CHECK (peak2_max > 2.0); + + CHECK (done > 50000); // »Tick« period is 50ms + // and this tick should determine end of timeline + + cout << "\nwaiting for shutdown of WorkForce"; + while (scheduler.workForce_.size() > 0) + { + sleep_for(10ms); + cout << "." << std::flush; + } + uint shutdown = offset(); + cout << "\nShutdown after "< 2.0e6); } diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 298864b1c..08ece4aae 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -7201,7 +7201,7 @@ The Scheduler is now considered an implementation-level facility with an interfa &rarr; [[Workers|SchedulerWorker]] -
+
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.
 
@@ -7226,6 +7226,18 @@ Drawing upon these principles, the treatment of worker calls can be aligned to t
 # 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 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.
+!!!Capacity redistribution scheme
+The effective behaviour results from the collaboration of {{{Scheduler::scatteredDelay()}}} with the {{{LoadController}}}. Redistribution can happen on //incoming capacity// (before the actual dispatch) and on //outgoing capacity//. After a »targeted sleep«, the thread is sent back with {{{PASS}}} and will thus immediately re-enter the capacity decision logic. The actual decision is then based on the current distance to the scheduler's »head element«
+|!next head|!|>|!incoming|>|!outgoing|
+|~|!distance|!tend-next|!regular|!tend-next|!regular|
+|unknown                 | ∞| IDLEWAIT | IDLEWAIT | WORKTIME | WORKTIME |
+|sleep-horizon | >20ms| IDLEWAIT | IDLEWAIT | TENDNEXT | WORKTIME |
+|work-horizon   | >5ms| IDLEWAIT | IDLEWAIT | TENDNEXT | WORKTIME |
+|near-horizon  | >50µs| TENDNEXT | NEARTIME | TENDNEXT | NEARTIME |
+|imminent         | >now| SPINTIME | SPINTIME | SPINTIME | SPINTIME |
+|past                 | <now| DISPATCH | DISPATCH| DISPATCH | DISPATCH |
+|case matrix for capacity redistribution|c
+&rarr; please have a look at {{{SchedulerLoadControl_test::classifyCapacity}}}
 
 !!!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:
diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm
index 0ea49df3e..75fce81ae 100644
--- a/wiki/thinkPad.ichthyo.mm
+++ b/wiki/thinkPad.ichthyo.mm
@@ -82566,7 +82566,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -82648,9 +82648,9 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + - + @@ -82910,9 +82910,9 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + - + @@ -83078,7 +83078,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -83175,11 +83175,11 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - + + + - + @@ -83193,7 +83193,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -83207,7 +83207,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -83244,8 +83244,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + @@ -83258,20 +83258,21 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - - + + + + + - + - + @@ -83604,7 +83605,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -83656,7 +83657,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -83682,8 +83683,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + @@ -83691,9 +83692,10 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + @@ -83761,10 +83763,10 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + - + @@ -83774,23 +83776,26 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - - - + + + + + - - - + + + - + + + - + + @@ -88963,9 +88968,9 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + - + @@ -89425,7 +89430,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -89590,18 +89595,19 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - + + + - - + + - + + @@ -89656,15 +89662,17 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + - + + + - - + + - + @@ -89686,17 +89694,15 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + - + - - - +

0000000599: INFO: suite.cpp:180: thread_1: invokeTestCase: ++------------------- invoking TEST: vault::gear::test::SchedulerService_test @@ -91334,7 +91340,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -91358,51 +91364,40 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

Und zwar weil für jeden targeted-Sleep und jeden Rückgabewert != PASS ja doch automatisch das Token gedropped wird. Deshalb ist nicht einmal der »Tick« ein Problem (ja der droppt es nicht). Nur wenn eine Interne Verarbeitung in Schleifen gehen würde, hätten wir ein Problem. Dazu müßte sie sich selbst ohne Zeitversatz wieder einplanen (bzw. die Scheduler-Zeitachse müßte geflutet sein)

- -
+
- + - - - +

Fazit: works as designed

- -
+ - - - +

die Kapazitäts-Verteilung wurde expizit geprüft und arbeitet sehr gut

- -
+ - - - +
  • @@ -91413,46 +91408,65 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- -
+
+ + + + - + + + + + + + + + - + - - - +

weil das Beladen und Starten des Schedulers erhebliche Zeit kostet (und nicht präzise ist)

- -
+
+ + - + - + + + - + + + + + + + - + +