diff --git a/src/vault/gear/load-controller.hpp b/src/vault/gear/load-controller.hpp index 0dc04c52f..1d86f42d0 100644 --- a/src/vault/gear/load-controller.hpp +++ b/src/vault/gear/load-controller.hpp @@ -82,7 +82,7 @@ //#include "vault/gear/activity-lang.hpp" //#include "lib/symbol.hpp" #include "lib/nocopy.hpp" -//#include "lib/util.hpp" +#include "lib/util.hpp" //#include #include @@ -152,13 +152,14 @@ namespace gear { public: /** - * did we already tend for the indicated next head time? + * did we already tend for the indicated next relevant head time? * @note const and non-grooming */ bool tendedNext (Time nextHead) const { - return nextHead == tendedHead_; + return not nextHead.isRegular() // note: empty queue reports Time::NEVER + or nextHead == tendedHead_; } /** @@ -202,7 +203,6 @@ namespace gear { Capacity markOutgoingCapacity (Time head, Time now) { - if (head == Time::NEVER) return IDLEWAIT; // empty queue auto horizon = classifyTimeHorizon (Offset{head - now}); return horizon > SPINTIME and not tendedNext(head)? TENDNEXT @@ -247,19 +247,20 @@ namespace gear { return TimeValue{wrap}; }; + TimeVar headDistance = util::max (tendedHead_-now, Time::ZERO); + switch (capacity) { case DISPATCH: return Offset::ZERO; case SPINTIME: return Offset::ZERO; case TENDNEXT: - return Offset{tendedHead_-now}; + return Offset{headDistance}; case NEARTIME: - return Offset{tendedHead_-now + scatter(WORK_HORIZON)}; + return Offset{headDistance + scatter(WORK_HORIZON)}; case WORKTIME: - return Offset{tendedHead_-now + scatter(SLEEP_HORIZON)}; case IDLEWAIT: - return Offset{/*no base offset*/ scatter(SLEEP_HORIZON)}; + return Offset{headDistance + scatter(SLEEP_HORIZON)}; default: NOTREACHED ("uncovered work capacity classification."); } diff --git a/src/vault/gear/scheduler.hpp b/src/vault/gear/scheduler.hpp index a861578fa..59edf4bb1 100644 --- a/src/vault/gear/scheduler.hpp +++ b/src/vault/gear/scheduler.hpp @@ -363,28 +363,36 @@ namespace gear { inline activity::Proc Scheduler::scatteredDelay (Time now, LoadController::Capacity capacity) { + auto doTargetedSleep = [&] + { + Offset targetedDelay = loadControl_.scatteredDelayTime (now, capacity); + std::this_thread::sleep_for (std::chrono::microseconds (_raw(targetedDelay))); + }; + auto doTendNextHead = [&] + { + Time head = layer1_.headTime(); + auto self = std::this_thread::get_id(); + if (not loadControl_.tendedNext(head) + and (layer2_.holdsGroomingToken(self) + or layer2_.acquireGoomingToken())) + loadControl_.tendNext(head); + }; + switch (capacity) { case LoadController::DISPATCH: return activity::PASS; case LoadController::SPINTIME: std::this_thread::yield(); - return activity::SKIP; + return activity::SKIP; // prompts to abort chain but call again immediately case LoadController::IDLEWAIT: - return activity::WAIT; + return activity::WAIT; // prompts to switch this thread into sleep mode case LoadController::TENDNEXT: - { - Time head = layer1_.headTime(); - auto self = std::this_thread::get_id(); - if (not loadControl_.tendedNext(head) - and (layer2_.holdsGroomingToken(self) - or layer2_.acquireGoomingToken())) - loadControl_.tendNext(head); - }// Fall-through to perform targeted wait - // @suppress("No break at end of case") + doTendNextHead(); + doTargetedSleep(); // let this thread wait until nest head time is due + return activity::SKIP; default: - Offset targetedDelay = loadControl_.scatteredDelayTime (now, capacity); - std::this_thread::sleep_for (std::chrono::microseconds (_raw(targetedDelay))); - return activity::SKIP; // indicates to abort this processing-chain for good + doTargetedSleep(); + return activity::SKIP; // prompts to abort this processing-chain for good } } diff --git a/tests/vault/gear/scheduler-load-control-test.cpp b/tests/vault/gear/scheduler-load-control-test.cpp index 4b9c7e47e..f7bf1e353 100644 --- a/tests/vault/gear/scheduler-load-control-test.cpp +++ b/tests/vault/gear/scheduler-load-control-test.cpp @@ -269,7 +269,9 @@ namespace test { CHECK ( ten == lctrl.scatteredDelayTime (now, Capacity::TENDNEXT) ); CHECK (is_between ( ten, ten+ WORK_HORIZON, lctrl.scatteredDelayTime (now, Capacity::NEARTIME))); CHECK (is_between ( ten, ten+SLEEP_HORIZON, lctrl.scatteredDelayTime (now, Capacity::WORKTIME))); - CHECK (is_between (Time::ZERO, SLEEP_HORIZON, lctrl.scatteredDelayTime (now, Capacity::IDLEWAIT))); + CHECK (is_between ( ten, ten+SLEEP_HORIZON, lctrl.scatteredDelayTime (now, Capacity::IDLEWAIT))); + + lctrl.tendNext(Time::ANYTIME); // reset to ensure we get no base offset // Offset is randomised based on the current time // Verify this yields an even distribution diff --git a/tests/vault/gear/scheduler-service-test.cpp b/tests/vault/gear/scheduler-service-test.cpp index d59683184..83c4375f1 100644 --- a/tests/vault/gear/scheduler-service-test.cpp +++ b/tests/vault/gear/scheduler-service-test.cpp @@ -100,6 +100,10 @@ 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() + * @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, + * we see additional cache effects after an extended sleep period. * @todo WIP 10/23 🔁 define ⟶ implement */ void @@ -153,7 +157,7 @@ namespace test { cout << "pullWork() on empty queue..."< up-front delay"< follow-up delay"< 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 @@ -215,11 +225,12 @@ SHOW_EXPR(scheduler.empty()) start += t1ms; // (just re-adjust the reference point to calculate slip_us) pullWork(); // re-invoke immediately as instructed CHECK (wasInvoked(start)); // Result: also the next Activity has been dispatched - CHECK (delay_us < 300); // not much slip and delay - CHECK (slip_us < 300); // Remark: here we often see cache-effects, since last EventLog call is way back, due to the long sleep - CHECK (activity::WAIT == res); // since queue is empty, we are instructed to sleep + 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 (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()< + + + + + + + + + @@ -82403,9 +82412,16 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + +
+ @@ -82588,6 +82604,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+
@@ -83151,7 +83168,9 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + + @@ -83161,6 +83180,19 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + + + + + + + @@ -88147,16 +88179,13 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

...und das erscheint adäquat für moderne Maschinen

- -
+
@@ -88177,13 +88206,19 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - - - + + - + + + + + + + + + + @@ -88220,40 +88255,44 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

also optimized vs. Debug-Build; ja man sieht diesen Unterschied im "slip", weil einfach die Verarbeitung länger dauert

- -
+
- + - - - +

habe generell hier den Fall der leeren Queue nicht bedacht

- -
+
- + + + + + + + + + + - + + @@ -88262,17 +88301,21 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - - + + + + - - + + - - + + + + + + @@ -88283,16 +88326,13 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

...und das ist so auch plausibel (interessanterweise gibt es hier keinen Unterschied debug/O3 )...  hier wird ein Vector befüllt und jede Menge String-Konvertierungen gemacht, und auch ein virtual call

- -
+ @@ -88393,8 +88433,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
steckt in Activity::callHook

- - + @@ -88492,9 +88531,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

...sondern nur die wenigen Translation-Units, die ich für relevant halte. Das könnte dann insgesamt nochmal einen Unterschied machen @@ -88520,16 +88557,13 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - +

Optimierung hat darauf keinerlei Einfluß

- -
+