Scheduler: complete and cover load indicator
- sample distance to scheduler head whenever a worker asks for work - moving average with N = worker-pool size and damp-factor 2 - multiply with the current concurrency fraction
This commit is contained in:
parent
a087e52ab1
commit
9f7711d26b
4 changed files with 291 additions and 54 deletions
|
|
@ -65,10 +65,19 @@
|
|||
** assigning new tasks, while workers returning from idle state are typically
|
||||
** sent back into idle state, unless there is direct need for more capacity.
|
||||
**
|
||||
** # Load indicator
|
||||
** A fusion of some operational values is used to build a heuristic indicator
|
||||
** of current scheduler load. These values can be retrieved with low overhead.
|
||||
** - the fraction of maximal concurrency actually used
|
||||
** - a sampling of the lag, i.e. the average distance to the next task;
|
||||
** this observation is sampled whenever a worker asks for more work.
|
||||
**
|
||||
** @see scheduler.hpp
|
||||
** @see SchedulerLoadControl_test
|
||||
** @see SchedulerService_test::verify_LoadFactor()
|
||||
** @see SchedulerStress_test
|
||||
**
|
||||
** @todo WIP-WIP-WIP 10/2023 »Playback Vertical Slice«
|
||||
** @todo WIP-WIP 11/2023 »Playback Vertical Slice«
|
||||
**
|
||||
*/
|
||||
|
||||
|
|
@ -109,6 +118,7 @@ namespace gear {
|
|||
using std::chrono_literals::operator ""us;
|
||||
using std::function;
|
||||
using std::atomic_int64_t;
|
||||
using std::memory_order_relaxed;
|
||||
|
||||
namespace { // Scheduler default config
|
||||
|
||||
|
|
@ -122,12 +132,16 @@ namespace gear {
|
|||
Duration SLEEP_HORIZON{_uTicks (20ms)};
|
||||
Duration WORK_HORIZON {_uTicks ( 5ms)};
|
||||
Duration NEAR_HORIZON {_uTicks (50us)};
|
||||
|
||||
const double LAG_SAMPLE_DAMPING = 2; ///< smoothing factor for exponential moving average of lag;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Controller to coordinate resource usage related to the Scheduler.
|
||||
* - implements the schematics for capacity redistribution
|
||||
* - provides some performance indicators, notably the LoadController::effectiveLoad()
|
||||
* @todo WIP-WIP 10/2023 gradually filling in functionality as needed
|
||||
* @see BlockFlow
|
||||
* @see Scheduler
|
||||
|
|
@ -161,32 +175,65 @@ namespace gear {
|
|||
|
||||
atomic_int64_t sampledLag_{0};
|
||||
|
||||
/**
|
||||
* @internal evaluate the situation encountered when a worker calls for work.
|
||||
* @remark this function updates an exponential moving average of schedule
|
||||
* head distance in a concurrency safe way. The value sampled is
|
||||
* clamped to prevent poisoning by excess peaks.
|
||||
* @warning Called from a hot path, with the potential to create congestion.
|
||||
* Measurements indicate single call < 200ns and < 5µs when contended.
|
||||
*/
|
||||
void
|
||||
markLagSample (Time head, Time now)
|
||||
{
|
||||
double headroom = _raw(std::clamp<TimeVar> (now - (head.isRegular()? head:now)
|
||||
, -SLEEP_HORIZON
|
||||
, WORK_HORIZON));
|
||||
const int64_t N = wiring_.maxCapacity * 3;
|
||||
int64_t average = sampledLag_.load (std::memory_order_relaxed);
|
||||
{ // negative when free capacity
|
||||
double lag = _raw(std::clamp<TimeVar> (now - (head.isRegular()? head:now)
|
||||
, -SLEEP_HORIZON
|
||||
, WORK_HORIZON));
|
||||
const double alpha = LAG_SAMPLE_DAMPING / (1 + wiring_.maxCapacity);
|
||||
int64_t average = sampledLag_.load (memory_order_relaxed);
|
||||
int64_t newAverage;
|
||||
do newAverage = std::floor ((headroom + (N-1)*average) / N);
|
||||
while (not sampledLag_.compare_exchange_weak (average, newAverage, std::memory_order_relaxed));
|
||||
do newAverage = std::floor (lag*alpha + (1-alpha)*average);
|
||||
while (not sampledLag_.compare_exchange_weak (average, newAverage, memory_order_relaxed));
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* @return guess of current scheduler pressure
|
||||
* @remark the value is sampled at the points where workers pull work.
|
||||
* Since these »capacity events« happen randomly, the current
|
||||
* distance to the schedule head hints at either free headroom
|
||||
* or overload leading to congestion.
|
||||
* @see #markLagSample
|
||||
*/
|
||||
int64_t
|
||||
lag()
|
||||
averageLag() const
|
||||
{
|
||||
return sampledLag_.load (std::memory_order_relaxed);
|
||||
return sampledLag_.load (memory_order_relaxed);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal (re)set the currently seen average lag.
|
||||
* @return the previous average value
|
||||
* @remark intended for unit testing and state reset;
|
||||
* thread-save. Regular use not recommended.
|
||||
*/
|
||||
int64_t
|
||||
setCurrentAverageLag (int64_t lag)
|
||||
{
|
||||
return sampledLag_.exchange(lag, memory_order_relaxed);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return guess of current load relative to full load
|
||||
* @remark based on the fusion of several state values,
|
||||
* which can be retrieved with low overhead
|
||||
* - the used fraction of possible concurrency
|
||||
* - [sampling of distance to next task](\ref averageLag)
|
||||
*/
|
||||
double
|
||||
effectiveLoad()
|
||||
effectiveLoad() const
|
||||
{
|
||||
double lag = sampledLag_.load (std::memory_order_relaxed);
|
||||
double lag = sampledLag_.load (memory_order_relaxed);
|
||||
lag -= 200;
|
||||
lag /= _raw(WORK_HORIZON);
|
||||
lag *= 10;
|
||||
|
|
|
|||
|
|
@ -34,9 +34,12 @@
|
|||
#include "lib/test/diagnostic-output.hpp"/////////////////////TODO
|
||||
|
||||
//#include <utility>
|
||||
#include <chrono>
|
||||
|
||||
using test::Test;
|
||||
using std::move;
|
||||
|
||||
using std::chrono::microseconds;
|
||||
//using util::isSameObject;
|
||||
|
||||
|
||||
|
|
@ -71,6 +74,7 @@ namespace test {
|
|||
tendNextActivity();
|
||||
classifyCapacity();
|
||||
scatteredReCheck();
|
||||
indicateAverageLoad();
|
||||
|
||||
walkingDeadline();
|
||||
}
|
||||
|
|
@ -288,6 +292,88 @@ namespace test {
|
|||
|
||||
|
||||
|
||||
|
||||
/** @test verify fusion of sampled observations to guess average scheduler load
|
||||
* - use a rigged wiring of the load controller to verify calculation
|
||||
* based on known values of current _concurrency_ and _schedule pressure_
|
||||
* - scheduling on average 200µs behind nominal schedule is considered
|
||||
* the regular balanced state and thus defined as 100% schedule pressure
|
||||
* - if congestion builds up to 1/10 of WORK_HORIZON, 200% overload is indicated
|
||||
* - on the other hand, if workers appear on average 200µs before the typical
|
||||
* balanced state, the resulting headroom is defined to constitute 50% pressure
|
||||
* - the pressure value is multiplied with the degree of concurrency
|
||||
* - the pressure is sampled from the lag (distance of current time to the
|
||||
* next activity to schedule), which is observed whenever a worker
|
||||
* calls in to retrieve more work. These calls happen randomly.
|
||||
* @todo WIP 10/23 ✔ define ⟶ ✔ implement
|
||||
*/
|
||||
void
|
||||
indicateAverageLoad()
|
||||
{
|
||||
uint maxThreads = 10;
|
||||
uint currThreads = 0;
|
||||
|
||||
LoadController::Wiring setup;
|
||||
setup.maxCapacity = maxThreads;
|
||||
setup.currWorkForceSize = [&]{ return currThreads; };
|
||||
// rigged setup to verify calculated load indicator
|
||||
LoadController lctrl{move(setup)};
|
||||
|
||||
CHECK (0 == lctrl.averageLag());
|
||||
CHECK (0 == lctrl.effectiveLoad());
|
||||
|
||||
// Manipulate the sampled average lag (in µs)
|
||||
lctrl.setCurrentAverageLag (200);
|
||||
// Scheduling 200µs behind nominal start time -> 100% schedule pressure
|
||||
|
||||
currThreads = 5;
|
||||
CHECK (0.5 == lctrl.effectiveLoad());
|
||||
currThreads = 8;
|
||||
CHECK (0.8 == lctrl.effectiveLoad());
|
||||
currThreads = 10;
|
||||
CHECK (1.0 == lctrl.effectiveLoad());
|
||||
|
||||
// congestion +500µs -> 200% schedule pressure
|
||||
lctrl.setCurrentAverageLag (200+500);
|
||||
CHECK (2.0 == lctrl.effectiveLoad());
|
||||
|
||||
lctrl.setCurrentAverageLag (200+500+500);
|
||||
CHECK (3.0 == lctrl.effectiveLoad()); // -> 300%
|
||||
|
||||
// if average headroom 500µs -> 50% load
|
||||
lctrl.setCurrentAverageLag (200-500);
|
||||
CHECK (0.5 == lctrl.effectiveLoad());
|
||||
CHECK (-300 == lctrl.averageLag());
|
||||
|
||||
lctrl.setCurrentAverageLag (200-500-500-500);
|
||||
CHECK (0.25 == lctrl.effectiveLoad());
|
||||
CHECK (-1300 == lctrl.averageLag());
|
||||
|
||||
// load indicator is always modulated by concurrency level
|
||||
currThreads = 2;
|
||||
CHECK (0.05 == lctrl.effectiveLoad());
|
||||
|
||||
// average lag is sampled from the situation when workers call in
|
||||
Time head = Time::ZERO;
|
||||
TimeVar curr = Time{1,0};
|
||||
lctrl.markIncomingCapacity (head,curr);
|
||||
CHECK (-882 == lctrl.averageLag());
|
||||
|
||||
lctrl.markIncomingCapacity (head,curr);
|
||||
CHECK (-540 == lctrl.averageLag());
|
||||
|
||||
curr = Time{0,1};
|
||||
lctrl.markIncomingCapacity (head,curr);
|
||||
lctrl.markIncomingCapacity (head,curr);
|
||||
CHECK (1291 == lctrl.averageLag());
|
||||
|
||||
curr = head - Time{0,2};
|
||||
lctrl.markIncomingCapacity (head,curr);
|
||||
CHECK (-2581 == lctrl.averageLag());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** @test TODO
|
||||
* @todo WIP 10/23 🔁 define ⟶ implement
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -167,7 +167,7 @@ SHOW_EXPR(wuff())
|
|||
sleep_for(50us);
|
||||
cout << wuff() << " +++ Load: "<<scheduler.getLoadIndicator()
|
||||
<<" --- HT= "<<_raw(scheduler.layer1_.headTime())-_raw(wau)
|
||||
<<" -+- Lag "<< scheduler.loadControl_.lag()
|
||||
<<" -+- Lag "<< scheduler.loadControl_.averageLag()
|
||||
<<endl;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82636,12 +82636,10 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1698632383220" ID="ID_716182887" MODIFIED="1698632491040" TEXT="Last-Schätzung entwickeln">
|
||||
<arrowlink COLOR="#4a5982" DESTINATION="ID_846411589" ENDARROW="Default" ENDINCLINATION="-55;-589;" ID="Arrow_ID_1233923536" STARTARROW="None" STARTINCLINATION="-390;272;"/>
|
||||
<icon BUILTIN="pencil"/>
|
||||
<node CREATED="1698632501888" ID="ID_334185073" MODIFIED="1698632518646" TEXT="1. Idee : WorkForce::size()">
|
||||
<node COLOR="#435e98" CREATED="1698632501888" FOLDED="true" ID="ID_334185073" MODIFIED="1698715205196" TEXT="1. Idee : WorkForce::size()">
|
||||
<node CREATED="1698632520658" ID="ID_1308650674" MODIFIED="1698632625312" TEXT="funktioniert so lala">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
...weil wir im Moment immer mit Vollast einsteigen und dann zwar im Idle-Fall herunterregeln; letzteres passiert typischerweise aber schnell. Also im Test sehe ich daher immer nur 1.0 oder keine Last
|
||||
|
|
@ -82652,9 +82650,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
</node>
|
||||
<node CREATED="1698632626540" ID="ID_1025237859" MODIFIED="1698632714226" TEXT="könnte trotzdem brauchbar sein">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
...das kann aber erst beurteilt werden, wenn wir echte Last-Szenarien kennen. Denkbar wäre, daß z.B. ein real-Time Render in etwa mit 3 Cores über die Runden kommt
|
||||
|
|
@ -82666,13 +82662,11 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
<arrowlink COLOR="#ce6c70" DESTINATION="ID_167635013" ENDARROW="Default" ENDINCLINATION="14;-26;" ID="Arrow_ID_1543325365" STARTARROW="None" STARTINCLINATION="-53;3;"/>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1698632730462" ID="ID_167635013" MODIFIED="1698632819293" TEXT="2. Idee : den Lag auswerten">
|
||||
<node COLOR="#435e98" CREATED="1698632730462" FOLDED="true" ID="ID_167635013" MODIFIED="1698715199604" TEXT="2. Idee : den Lag auswerten">
|
||||
<linktarget COLOR="#ce6c70" DESTINATION="ID_167635013" ENDARROW="Default" ENDINCLINATION="14;-26;" ID="Arrow_ID_1543325365" SOURCE="ID_348864601" STARTARROW="None" STARTINCLINATION="-53;3;"/>
|
||||
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1698632823807" ID="ID_1604616715" MODIFIED="1698632937183" TEXT="das ist aber gar nicht so einfach">
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1698632823807" ID="ID_1604616715" MODIFIED="1698702752645" TEXT="das ist aber gar nicht so einfach">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<head/>
|
||||
<body>
|
||||
<ul>
|
||||
<li>
|
||||
|
|
@ -82689,7 +82683,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
</html></richcontent>
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
</node>
|
||||
<node COLOR="#435e98" CREATED="1698632941116" ID="ID_1797377910" MODIFIED="1698633204991" TEXT="kommt man irgendwie an Werte pro Worker heran?">
|
||||
<node COLOR="#435e98" CREATED="1698632941116" FOLDED="true" ID="ID_1797377910" MODIFIED="1698633204991" TEXT="kommt man irgendwie an Werte pro Worker heran?">
|
||||
<icon BUILTIN="help"/>
|
||||
<node CREATED="1698632978397" ID="ID_1774631238" MODIFIED="1698632990815" TEXT="im Worker speichern scheide ich sofort aus (Architektur)"/>
|
||||
<node CREATED="1698632992259" ID="ID_1128001448" MODIFIED="1698633006765" TEXT="mit Thread-Locals will ich schon gleich gar nicht anfangen"/>
|
||||
|
|
@ -82703,19 +82697,18 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
<node CREATED="1698633186745" ID="ID_1822633925" MODIFIED="1698633194284" TEXT="darüber dann ein moving-Average"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1698633206582" ID="ID_1221695664" MODIFIED="1698633222669" TEXT="moving-Average in concurrent environment">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node COLOR="#338800" CREATED="1698633206582" FOLDED="true" ID="ID_1221695664" MODIFIED="1698711102459" TEXT="moving-Average in concurrent environment">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node CREATED="1698633226641" ID="ID_1247903339" MODIFIED="1698633242622" TEXT="die »capacity events« sind wirklich zufällig"/>
|
||||
<node CREATED="1698633248600" ID="ID_1947820475" MODIFIED="1698633269818" TEXT="Grooming-Token kommt nicht in Frage, Locking sowiso nicht"/>
|
||||
<node CREATED="1698633270718" ID="ID_1128407195" MODIFIED="1698633298798" TEXT="als fortlaufendes Produkt mit einem Atomic berechnen">
|
||||
<icon BUILTIN="idea"/>
|
||||
<node COLOR="#5b280f" CREATED="1698633270718" ID="ID_1128407195" MODIFIED="1698702657581" TEXT="als fortlaufendes Produkt mit einem Atomic berechnen">
|
||||
<arrowlink COLOR="#ef3052" DESTINATION="ID_1504851010" ENDARROW="Default" ENDINCLINATION="-204;13;" ID="Arrow_ID_339066446" STARTARROW="None" STARTINCLINATION="81;212;"/>
|
||||
<icon BUILTIN="button_cancel"/>
|
||||
<node CREATED="1698633317512" ID="ID_1161198928" MODIFIED="1698633425867" TEXT="Moving-Average läßt sich in Faktoren-Form bringen">
|
||||
<linktarget COLOR="#757ecb" DESTINATION="ID_1161198928" ENDARROW="Default" ENDINCLINATION="1079;62;" ID="Arrow_ID_29993738" SOURCE="ID_649514706" STARTARROW="None" STARTINCLINATION="760;42;"/>
|
||||
<node CREATED="1698633491729" HGAP="44" ID="ID_1503820730" MODIFIED="1698633786583" TEXT="muß dazu aber aktuelles MA herausdividieren" VSHIFT="-6">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
<u> Exponential MA</u>:
|
||||
|
|
@ -82739,9 +82732,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
</node>
|
||||
<node CREATED="1698633300801" ID="ID_614548490" MODIFIED="1698633874064" TEXT="Grundidee: »eventual consistency«">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
Bei einem moving-Average interessiert es uns eigentlich nicht, <i>wann genau </i>der Wert gilt und <i>für wen </i>er gilt. Es interessiert uns nur, was am Ende rausgekommen ist, bzw. wie der Trend im Moment so ist
|
||||
|
|
@ -82751,9 +82742,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
</node>
|
||||
<node CREATED="1698633875244" ID="ID_202660501" MODIFIED="1698633929103">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
wir ziehen das aktuelle MA »jetzt«
|
||||
|
|
@ -82770,9 +82759,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
</node>
|
||||
<node COLOR="#5b280f" CREATED="1698633939212" ID="ID_1007287132" MODIFIED="1698635722966">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
und <i>das </i>läßt sich mit <b>zwei </b>Atomic-Operationen realisieren
|
||||
|
|
@ -82780,17 +82767,36 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
</body>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="button_cancel"/>
|
||||
<node CREATED="1698635724493" ID="ID_1504851010" MODIFIED="1698635742694" TEXT="Nein! es gibt keine atomare Multiplikation"/>
|
||||
<node CREATED="1698635745173" ID="ID_509243439" MODIFIED="1698635754653" TEXT="damit muß ich doch die compare-exchange-Schleife machen"/>
|
||||
<node CREATED="1698635724493" ID="ID_1504851010" MODIFIED="1698702651541" TEXT="Nein! es gibt keine atomare Multiplikation">
|
||||
<linktarget COLOR="#ef3052" DESTINATION="ID_1504851010" ENDARROW="Default" ENDINCLINATION="-204;13;" ID="Arrow_ID_339066446" SOURCE="ID_1128407195" STARTARROW="None" STARTINCLINATION="81;212;"/>
|
||||
<icon BUILTIN="stop-sign"/>
|
||||
</node>
|
||||
<node CREATED="1698702601241" ID="ID_987600121" MODIFIED="1698702624304" TEXT="(wäre auch nicht wirklich sinnvoll...)">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
wenn man auch nur ansatzweise bedenkt, wie Assembly funktioniert
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node CREATED="1698635745173" ID="ID_509243439" MODIFIED="1698702739866" TEXT="damit muß ich doch die compare-exchange-Schleife machen">
|
||||
<arrowlink COLOR="#4b5f99" DESTINATION="ID_995319675" ENDARROW="Default" ENDINCLINATION="311;-25;" ID="Arrow_ID_1824704831" STARTARROW="None" STARTINCLINATION="-347;17;"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1698633984534" ID="ID_526389523" MODIFIED="1698634002948" TEXT="Prototyp-Implementierung in die markIncomingCapcity einhängen">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node COLOR="#435e98" CREATED="1698702687206" ID="ID_995319675" MODIFIED="1698702733706" TEXT="dann optimistic-compare-exchange">
|
||||
<linktarget COLOR="#4b5f99" DESTINATION="ID_995319675" ENDARROW="Default" ENDINCLINATION="311;-25;" ID="Arrow_ID_1824704831" SOURCE="ID_509243439" STARTARROW="None" STARTINCLINATION="-347;17;"/>
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1698633984534" FOLDED="true" ID="ID_526389523" MODIFIED="1698704884755" TEXT="Prototyp-Implementierung in die markIncomingCapcity einhängen">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node CREATED="1698635767135" ID="ID_1284337606" MODIFIED="1698635791023" TEXT="erst einmal nur den Offset samplen und beobachten"/>
|
||||
<node CREATED="1698635791606" ID="ID_1832030937" MODIFIED="1698635805382" TEXT="Berechnung per compare-and-swap"/>
|
||||
<node CREATED="1698638460422" ID="ID_583392778" MODIFIED="1698638471065" TEXT="Werte sehen plausibel aus"/>
|
||||
<node COLOR="#435e98" CREATED="1698638471844" ID="ID_429172374" MODIFIED="1698679552921" TEXT="wird aber extrem häufig aufgerufen — warum?">
|
||||
<node COLOR="#435e98" CREATED="1698638471844" FOLDED="true" ID="ID_429172374" MODIFIED="1698679552921" TEXT="wird aber extrem häufig aufgerufen — warum?">
|
||||
<arrowlink COLOR="#ab2254" DESTINATION="ID_1087870120" ENDARROW="Default" ENDINCLINATION="-356;12;" ID="Arrow_ID_187678307" STARTARROW="None" STARTINCLINATION="215;13;"/>
|
||||
<icon BUILTIN="broken-line"/>
|
||||
<node COLOR="#5b280f" CREATED="1698638515487" ID="ID_847572680" MODIFIED="1698677275734" TEXT="sitzt in einem Poll-Zyklus?!">
|
||||
|
|
@ -82837,17 +82843,54 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1698680053860" ID="ID_1477905589" MODIFIED="1698680070366" TEXT="Microbenchmark der Meß-Funktion">
|
||||
<node CREATED="1698680053860" ID="ID_1477905589" MODIFIED="1698680311278">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
<b>Microbenchmark</b> der Meß-Funktion
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="info"/>
|
||||
<node CREATED="1698680080248" ID="ID_1604376595" MODIFIED="1698680100226" TEXT="100'000 Iterationen (pro thread)"/>
|
||||
<node CREATED="1698680071746" ID="ID_1915767027" MODIFIED="1698680266483" TEXT="single-threaded: 120ns"/>
|
||||
<node CREATED="1698680116412" ID="ID_1012490645" MODIFIED="1698680136077" TEXT="7-threads-contending: 4.5µs"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1698702792264" ID="ID_803243904" MODIFIED="1698702815999" TEXT="Verlauf sieht im Test sinnvoll aus">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1697808402431" ID="ID_1499932709" MODIFIED="1697808419725" TEXT="beobachtet / weiß von der WorkForce-Stärke">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node COLOR="#338800" CREATED="1698702824964" ID="ID_28379545" MODIFIED="1698715179796" TEXT="Last-Schätzung aus Parallel-Load und Lag">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#338800" CREATED="1698702851630" ID="ID_936369816" MODIFIED="1698702859151" TEXT="vorläufige Implementierung">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node CREATED="1698711855235" ID="ID_855137031" LINK="#ID_945067314" MODIFIED="1698711885087" TEXT="Smoothing-Faktor α geeignet wählen"/>
|
||||
<node COLOR="#338800" CREATED="1698702859767" ID="ID_1863816756" MODIFIED="1698715174255" TEXT="Berechnung per Einzeltest abdecken">
|
||||
<arrowlink COLOR="#6a9eb2" DESTINATION="ID_1985241613" ENDARROW="Default" ENDINCLINATION="1958;-153;" ID="Arrow_ID_13582151" STARTARROW="None" STARTINCLINATION="598;41;"/>
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1697808402431" ID="ID_1499932709" MODIFIED="1698715221160" TEXT="beobachtet / weiß von der WorkForce-Stärke">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node COLOR="#338800" CREATED="1698715222158" ID="ID_1634688486" MODIFIED="1698715238526" TEXT="realisiert per function-binding">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1698715228810" ID="ID_404234345" MODIFIED="1698715283830" TEXT="im Scheduler verdrahtet">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
<font face="Monospaced" color="#372493">Scheduler::connectMonitoring()</font>
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1698544547801" ID="ID_1011726550" MODIFIED="1698544832553" TEXT="regelmäßige Integration / state fusion">
|
||||
|
|
@ -88848,6 +88891,46 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
<node COLOR="#338800" CREATED="1698243357550" ID="ID_458370410" MODIFIED="1698243362335" TEXT="scatteredReCheck">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1698702871326" ID="ID_1985241613" MODIFIED="1698715174256" TEXT="indicateAverageLoad">
|
||||
<linktarget COLOR="#6a9eb2" DESTINATION="ID_1985241613" ENDARROW="Default" ENDINCLINATION="1958;-153;" ID="Arrow_ID_13582151" SOURCE="ID_1863816756" STARTARROW="None" STARTINCLINATION="598;41;"/>
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1698711912906" HGAP="45" ID="ID_1431349753" MODIFIED="1698715153652" TEXT="der Test ist schwierig einzurichten" VSHIFT="-3">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
Und zwar weil wir
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
ein Moving-Average im Spiel haben, das es sehr schwer macht, bestimmte Werte zu erzielen
|
||||
</li>
|
||||
<li>
|
||||
das Moving-Average in einer privaten Variablen steckt (ist auch gut so, da es ein Atomic sein muß wegen concurrency)
|
||||
</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
</node>
|
||||
<node COLOR="#435e98" CREATED="1698712060968" HGAP="29" ID="ID_16893514" MODIFIED="1698715151332" TEXT="die beiden Belange trennen">
|
||||
<icon BUILTIN="yes"/>
|
||||
<node COLOR="#338800" CREATED="1698712079191" ID="ID_860323615" MODIFIED="1698715158820" TEXT="führe einen Setter ein für den Test">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
Alternative wäre, den Test zum Friend zu machen. Das will ich aber möglichst nicht zur Gewohnheit werden lassen. Hier könnte man das umgehen, da wegen der Concurrency ein Setter sogar sinnvoll ist: der würde dann einen atomic-swap machen
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1698712234889" ID="ID_1843404710" MODIFIED="1698715161154" TEXT="dann kann man den Moving-Average einmal isoliert demonstrieren">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1688336835085" ID="ID_1753236898" MODIFIED="1697732777759" TEXT="SchedulerService_test">
|
||||
|
|
@ -89497,9 +89580,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
<node CREATED="1698627399974" ID="ID_1608280091" MODIFIED="1698628172170" TEXT="ExecutionCtx::post : 1.3µs"/>
|
||||
<node COLOR="#5b280f" CREATED="1698628174302" ID="ID_127791676" MODIFIED="1698628251530" TEXT="das ist aber nicht sonderlich hilfreich">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
...weil es eben ein Microbenchmark des reinen Dispatch-Aufrufs ist, und ohne das ganze Drumherum mit Grooming-Token, Queue-Verwaltung und Kapazitäts-Verteilung
|
||||
|
|
@ -90830,6 +90911,29 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
</body>
|
||||
</html></richcontent>
|
||||
<arrowlink COLOR="#757ecb" DESTINATION="ID_1161198928" ENDARROW="Default" ENDINCLINATION="1079;62;" ID="Arrow_ID_29993738" STARTARROW="None" STARTINCLINATION="760;42;"/>
|
||||
<node CREATED="1698711155793" HGAP="37" ID="ID_945067314" MODIFIED="1698711686721" TEXT="Dämpfungsfaktor α" VSHIFT="11">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
EMA(i) = α · value(i) + (1- α) · EMA(i-1)
|
||||
</p>
|
||||
<p>
|
||||
|
||||
</p>
|
||||
<p>
|
||||
Im Grunde gibt es keine Herleitung, denn das ganze Verfahren ist pragmatisch.
|
||||
</p>
|
||||
<p>
|
||||
In meiner Rationalisierung wäre dann α ≔ 1/N  ⟹ 1-α = (N-1)/N
|
||||
</p>
|
||||
<p>
|
||||
In der Wirtschaft (Chart-Analyse) findet man oft α ≔ damp / (N+1)  und damp ≔ 2 ist die geläufige Wahl. Damit kommt man auf die oft zitierte Faustregel, daß ein neuer Wert in einen 10-day EMA mit 18% eingeht (2/11)
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1689355234696" ID="ID_1358310078" MODIFIED="1689355261867" TEXT="Betrachtung des Einzelwerts in der k-ten Iteration">
|
||||
<node CREATED="1689355265862" ID="ID_19716247" MODIFIED="1689355311124" TEXT="xₖ = x/N · ((N-1)/N)^k"/>
|
||||
|
|
|
|||
Loading…
Reference in a new issue