From 8056bebf9ccfab8866b97b8ab882f746389a0860 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 7 Nov 2023 16:12:56 +0100 Subject: [PATCH] Scheduler: allow to manipulate nominal full capacity While building increasingly complex integration tests for the Scheduler, it turns out helpful to be able to manipulate the "full concurreency" as used by Scheduler, WorkForce and LoadController. In the current test, I am facing a problem that new entries from the threadsafe entrance queue are not propagated to the priority queue soon enough; partly this is due to functionality still to be added (scaling up when new tasks are passed in) -- but this will further complicate the test setup. --- src/lib/util.hpp | 10 +- src/vault/gear/load-controller.hpp | 7 +- src/vault/gear/scheduler.hpp | 2 +- src/vault/gear/work-force.cpp | 38 ++-- src/vault/gear/work-force.hpp | 4 +- .../gear/scheduler-load-control-test.cpp | 2 +- tests/vault/gear/scheduler-service-test.cpp | 27 ++- wiki/thinkPad.ichthyo.mm | 207 +++++++++++++++++- 8 files changed, 246 insertions(+), 51 deletions(-) diff --git a/src/lib/util.hpp b/src/lib/util.hpp index e915660b4..981f3aaa5 100644 --- a/src/lib/util.hpp +++ b/src/lib/util.hpp @@ -52,21 +52,21 @@ namespace util { template - inline int + inline int constexpr sgn (NUM n) { return (n==0)? 0 :((n<0)? -1:+1 ); } template - inline N1 + inline N1 constexpr min (N1 n1, N2 n2) { return n2 < n1? N1(n2) : n1; } template - inline N1 + inline N1 constexpr max (N1 n1, N2 n2) { return n1 < n2? N1(n2) : n1; @@ -74,7 +74,7 @@ namespace util { /** cut a numeric value to be >=0 */ template - inline NUM + inline NUM constexpr noneg (NUM val) { return (0 - inline NUM + inline NUM constexpr limited (NB lowerBound, NUM val, NB upperBound) { return min ( max (val, lowerBound) diff --git a/src/vault/gear/load-controller.hpp b/src/vault/gear/load-controller.hpp index 6068df373..329220c91 100644 --- a/src/vault/gear/load-controller.hpp +++ b/src/vault/gear/load-controller.hpp @@ -152,8 +152,7 @@ namespace gear { public: struct Wiring { - size_t maxCapacity{2}; - + function maxCapacity {[]{ return 1; }}; function currWorkForceSize{[]{ return 0; }}; ///////TODO add here functors to access performance indicators }; @@ -189,7 +188,7 @@ namespace gear { double lag = _raw(std::clamp (now - (head.isRegular()? head:now) , -SLEEP_HORIZON , WORK_HORIZON)); - const double alpha = LAG_SAMPLE_DAMPING / (1 + wiring_.maxCapacity); + const double alpha = LAG_SAMPLE_DAMPING / (1 + wiring_.maxCapacity()); int64_t average = sampledLag_.load (memory_order_relaxed); int64_t newAverage; do newAverage = std::floor (lag*alpha + (1-alpha)*average); @@ -238,7 +237,7 @@ namespace gear { lag /= _raw(WORK_HORIZON); lag *= 10; double lagFactor = lag<0? 1/(1-lag): 1+lag; - double loadFactor = wiring_.currWorkForceSize() / double(wiring_.maxCapacity); + double loadFactor = wiring_.currWorkForceSize() / double(wiring_.maxCapacity()); return loadFactor * lagFactor; } diff --git a/src/vault/gear/scheduler.hpp b/src/vault/gear/scheduler.hpp index 9d78ef947..ad702dccc 100644 --- a/src/vault/gear/scheduler.hpp +++ b/src/vault/gear/scheduler.hpp @@ -360,7 +360,7 @@ namespace gear { connectMonitoring() { LoadController::Wiring setup; - setup.maxCapacity = work::Config::COMPUTATION_CAPACITY; + setup.maxCapacity = []{ return work::Config::COMPUTATION_CAPACITY; }; setup.currWorkForceSize = [this]{ return workForce_.size(); }; return setup; } diff --git a/src/vault/gear/work-force.cpp b/src/vault/gear/work-force.cpp index 442f0daa1..55b944480 100644 --- a/src/vault/gear/work-force.cpp +++ b/src/vault/gear/work-force.cpp @@ -23,27 +23,14 @@ /** @file work-force.cpp ** Implementation of render worker coordination. ** - ** @note as of X/2023 this is complete bs - ** @todo WIP ///////////////////////TICKET # - ** - ** @see ////TODO_test usage example - ** @see scheduler.cpp implementation - ** - ** @todo WIP-WIP-WIP 6/2023 »Playback Vertical Slice« + ** @todo WIP-WIP 11/2023 »Playback Vertical Slice« ** */ #include "vault/gear/work-force.hpp" -//#include "lib/symbol.hpp" -//#include "include/logging.h" #include "lib/util.hpp" -//#include - -//using std::string; -//using util::isnil; - namespace vault{ namespace gear { @@ -56,15 +43,24 @@ namespace gear { -// NA::~NA() { } - - /** default value for full computing capacity is to use all (virtual) cores */ - const size_t work::Config::COMPUTATION_CAPACITY = util::max (std::thread::hardware_concurrency() - , MINIMAL_CONCURRENCY); - - + /** + * Nominal »full size« of a pool of concurrent workers. + * This value is [initialised](\ref Config::getDefaultComputationCapacity) + * to use all available concurrent computing cores, but can be adjusted. + * Adjustments should be done before a worker pool scales up. + * @warning this value is taken as-is; setting it to zero will disable + * many (but not all) aspects of concurrent processing. + */ + size_t work::Config::COMPUTATION_CAPACITY = Config::getDefaultComputationCapacity(); /** + * default value for full computing capacity is to use all (virtual) cores. */ + size_t + work::Config::getDefaultComputationCapacity() + { + return util::max (std::thread::hardware_concurrency() + , MINIMAL_CONCURRENCY); + } }} // namespace vault::gear diff --git a/src/vault/gear/work-force.hpp b/src/vault/gear/work-force.hpp index 9f7978a53..a9a565f2c 100644 --- a/src/vault/gear/work-force.hpp +++ b/src/vault/gear/work-force.hpp @@ -95,10 +95,12 @@ namespace gear { */ struct Config { - static const size_t COMPUTATION_CAPACITY; + static size_t COMPUTATION_CAPACITY; const milliseconds IDLE_WAIT = 20ms; const size_t DISMISS_CYCLES = 100; + + static size_t getDefaultComputationCapacity(); }; diff --git a/tests/vault/gear/scheduler-load-control-test.cpp b/tests/vault/gear/scheduler-load-control-test.cpp index 9923fcdcd..094b9eaf1 100644 --- a/tests/vault/gear/scheduler-load-control-test.cpp +++ b/tests/vault/gear/scheduler-load-control-test.cpp @@ -316,7 +316,7 @@ namespace test { uint currThreads = 0; LoadController::Wiring setup; - setup.maxCapacity = maxThreads; + setup.maxCapacity = [&]{ return maxThreads; }; setup.currWorkForceSize = [&]{ return currThreads; }; // rigged setup to verify calculated load indicator LoadController lctrl{move(setup)}; diff --git a/tests/vault/gear/scheduler-service-test.cpp b/tests/vault/gear/scheduler-service-test.cpp index 73008e630..fa78239e5 100644 --- a/tests/vault/gear/scheduler-service-test.cpp +++ b/tests/vault/gear/scheduler-service-test.cpp @@ -80,10 +80,10 @@ namespace test { virtual void run (Arg) { - simpleUsage(); - verify_StartStop(); - verify_LoadFactor(); - invokeWorkFunction(); +// simpleUsage(); +// verify_StartStop(); +// verify_LoadFactor(); +// invokeWorkFunction(); scheduleRenderJob(); walkingDeadline(); } @@ -464,24 +464,27 @@ namespace test { ////////////////////////////////// CHECK (scheduler.empty()); SHOW_EXPR(offset()) + auto buidl= scheduler.defineSchedule(testJob) .startOffset(200us) - .lifeWindow (1ms) - .manifestation(ManifestationID{55}) - .post(); + .lifeWindow (1ms); +SHOW_EXPR(offset()) + buidl .post(); CHECK (not scheduler.empty()); - CHECK (detector.ensureNoInvocation("testJob")); +// CHECK (detector.ensureNoInvocation("testJob")); SHOW_EXPR(offset()) sleep_for(400us); - CHECK (detector.ensureNoInvocation("testJob")); +// CHECK (detector.ensureNoInvocation("testJob")); SHOW_EXPR(offset()) - + scheduler.layer1_.feedPrioritisation(); auto res= scheduler.getWork(); -SHOW_EXPR(res) +SHOW_EXPR(offset()) +SHOW_EXPR(res) +SHOW_EXPR(offset(scheduler.layer1_.headTime())) // CHECK (activity::PASS == scheduler.getWork()); - CHECK (scheduler.empty()); +// CHECK (scheduler.empty()); cout << detector.showLog()< + + + + + + + + + + + + + + @@ -82669,6 +82683,37 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + + + + + + + + + + + + + + +

+ ....also kein Locking etc; allerdings greift der LoadController jetzt per Lambda darauf zu. +

+ + +
+
+ + + +
@@ -82910,6 +82955,73 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + + + + + + + + + + + + + + + + + +

+ ...im Lastbetrieb möchte man das eigentlich auch nicht: ein ankommender Thread soll gemäß aktueller Situation klassifiziert werden, aber nicht erst mal um das Grooming-Token konkurrieren +

+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

+ ....und das würde in dem Fall das Problem durch den ersten »Tick« nebenbei beheben — aber nicht, falls der Scheduler bereits läuft und leer gefallen ist +

+ + +
+ +
+
+
+
@@ -84099,7 +84211,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + @@ -84154,13 +84267,35 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + +
+ + + + + + + + + + + +

+ daher muß für einen neuen Job ein step up erfolgen +

+ + +
+ +
+ +
@@ -84302,6 +84437,30 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + + + + + + + + + + + + + + + + + + @@ -84920,7 +85079,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -87392,7 +87551,11 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + + + + @@ -92249,11 +92412,43 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + + + + + + + + + + +

+ Aus Sicht des Testaufbaues wäre es sehr wünschenswert. Aber da das Scheduler-API high-Level ist, sehe ich keine einfache Möglichkeit, für einen Test dazwischen zu gehen. Ein Mocking / Austauschen des Scheduler wäre hier nicht zielführend (weil es genau auf die Interaktion mit der integrierten Scheduler-Implementierung ankommt). +

+ +
+
+ + + + + + + + + + + + + + + + +