Block-Flow: compute exponential moving average

..as a heuristic to regulate optimal Epoch duration;
when Epochs are discarded, the effective fill factor can be used
to guess an Epoch duration time, which would (in hindsight)
lead to perfect usage of storage space
This commit is contained in:
Fischlurch 2023-07-17 03:00:56 +02:00
parent bd353d768a
commit 9d040dc49c
3 changed files with 56 additions and 22 deletions

View file

@ -98,6 +98,7 @@ namespace gear {
using util::Rat;
using util::isnil;
using lib::time::Time;
using lib::time::FSecs;
using lib::time::TimeVar;
using lib::time::Duration;
using lib::time::FrameRate;
@ -436,13 +437,21 @@ namespace gear {
* actual Epoch duration by the fill factor (longer Epoch => less capacity).
* To avoid control oscillations however, it seems prudent to use damping by
* an exponential moving average, nominally over #AVERAGE_EPOCHS.
* The current epochStep_ is assumed to be such a moving average, and will be
* updated accordingly.
* The current epochStep_ is assumed to be such a moving average,
* and will be updated accordingly.
* @todo the unclear status of the time base type hampers calculation
* with fractional time values, as is necessary here. As workaround,
* the argument is typed as TimeVar, which opens a calculation path
* without much spurious range checks. /////////////////////////////////////////////////////////TICKET #1259 : reorganise raw time base datatypes : need conversion path into FSecs
*/
void
markEpochUnderflow (Duration actualLen, Rat fillFactor)
markEpochUnderflow (TimeVar actualLen, Rat fillFactor)
{
UNIMPLEMENTED ("adjust size when detecting partially filled Epochs");
Rat contribution = Rat{_raw(actualLen), _raw(epochStep_)} / fillFactor;
// Exponential MA: mean ≔ mean * (N-1)/N + newVal/N
const Rat N = AVERAGE_EPOCHS;
Rat avgFactor = (contribution + N-1) / N;
adjustEpochStep (avgFactor);
}

View file

@ -270,7 +270,9 @@ namespace test {
CHECK (watch(bFlow).allEpochs() == "10s200ms|10s400ms|10s600ms|10s800ms|11s|11s131ms|11s262ms|11s393ms|11s524ms"_expect);
CHECK (watch(bFlow).find(a7) == "11s524ms"_expect);
SHOW_EXPR(bFlow.getEpochStep())
bFlow.discardBefore (Time{999,10});
SHOW_EXPR(bFlow.getEpochStep())
CHECK (watch(bFlow).allEpochs() == "11s|11s131ms|11s262ms|11s393ms|11s524ms"_expect);
// placed into the oldest Epoch still alive
@ -280,7 +282,10 @@ namespace test {
/** @test TODO load based regulation of Epoch spacing
/** @test load based regulation of Epoch spacing
* - on overflow, capacity is boosted by a fixed factor
* - on clean-up, a moving average of (in hindsight) optimal length is
* computed and used as the new Epoch spacing
* @todo WIP 7/23 define 🔁implement
*/
void
@ -289,27 +294,32 @@ namespace test {
BlockFlow bFlow;
CHECK (bFlow.getEpochStep() == INITIAL_EPOCH_STEP);
// whenever an Epoch overflow happens, capacity is boosted by reducing the Epoch duration
bFlow.markEpochOverflow();
CHECK (bFlow.getEpochStep() == INITIAL_EPOCH_STEP * OVERFLOW_BOOST_FACTOR);
bFlow.markEpochOverflow();
CHECK (bFlow.getEpochStep() == INITIAL_EPOCH_STEP * OVERFLOW_BOOST_FACTOR*OVERFLOW_BOOST_FACTOR);
Duration dur1 = INITIAL_EPOCH_STEP;
Duration dur2 = INITIAL_EPOCH_STEP * OVERFLOW_BOOST_FACTOR;
// To counteract this increase, on clean-up the actual fill rate of the Extent
// serves to guess an optimal Epoch duration, which is averaged exponentially
// Using just arbitrary demo values for some fictional Epochs
TimeVar dur1 = INITIAL_EPOCH_STEP;
Rat fill1 = 8_r/10;
TimeVar dur2 = INITIAL_EPOCH_STEP * OVERFLOW_BOOST_FACTOR;
Rat fill2 = 3_r/10;
TimeVar step = bFlow.getEpochStep();
Rat fill = 8_r/10;
Rat N = AVERAGE_EPOCHS;
TimeVar step = bFlow.getEpochStep();
bFlow.markEpochUnderflow (dur1, fill);
CHECK (bFlow.getEpochStep() == step*((N-1)/N) + dur1*(1/N /fill));
bFlow.markEpochUnderflow (dur1, fill1);
CHECK (bFlow.getEpochStep() == Duration{FSecs{step}*(N-1)/N + FSecs{dur1}/fill1/N});
step = bFlow.getEpochStep();
fill = 3_r/10;
bFlow.markEpochUnderflow (dur2, fill);
CHECK (bFlow.getEpochStep() == step*((N-1)/N) + dur2*(1/N /fill));
}
bFlow.markEpochUnderflow (dur2, fill2);
CHECK (bFlow.getEpochStep() == Duration{FSecs{step}*(N-1)/N + FSecs{dur2}/fill2/N});
} // Note: for verification the exponential average is computed via FSecs
// which is a different calculation path but yields the same result
/** @test TODO maintain progression of epochs.

View file

@ -79747,8 +79747,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
Entscheidung: Allokation entgleist nur<i>&#160;ausnahmsweise</i>
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<arrowlink COLOR="#fd1a6e" DESTINATION="ID_1315709817" ENDARROW="Default" ENDINCLINATION="21;-31;" ID="Arrow_ID_1483237280" STARTARROW="None" STARTINCLINATION="-248;12;"/>
<icon BUILTIN="yes"/>
</node>
@ -80161,15 +80160,17 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<font ITALIC="true" NAME="SansSerif" SIZE="14"/>
<icon BUILTIN="yes"/>
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1689204305717" ID="ID_123055064" MODIFIED="1689204325821" TEXT="sp&#xe4;ter wird auch dieses Thema auf TimingObservable aufsetzen">
<linktarget COLOR="#8d8fd3" DESTINATION="ID_123055064" ENDARROW="Default" ENDINCLINATION="606;0;" ID="Arrow_ID_1147910894" SOURCE="ID_900092388" STARTARROW="None" STARTINCLINATION="216;0;"/>
<icon BUILTIN="yes"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1689204331764" ID="ID_578018270" MODIFIED="1689204425304" TEXT="vorerst einfache ged&#xe4;mpfte Mittelung">
<node COLOR="#435e98" CREATED="1689204331764" ID="ID_578018270" MODIFIED="1689555121736" TEXT="vorerst einfache ged&#xe4;mpfte Mittelung">
<icon BUILTIN="yes"/>
<node CREATED="1689354964927" ID="ID_1525931195" MODIFIED="1689354982888" TEXT="mir schwebt ein exponentielles moving-average vor"/>
<node CREATED="1689355051116" ID="ID_214862798" MODIFIED="1689355076675" TEXT="Ansatz">
<node CREATED="1689355077335" ID="ID_250434643" MODIFIED="1689355094729" TEXT="lineares moving-average: den N-ten alten Wert subtrahieren"/>
<node CREATED="1689355095725" ID="ID_917795868" MODIFIED="1689356305847" TEXT="exponentielles moving-average: ein N-tel vom gemittelten Wert subtrahieren">
<linktarget COLOR="#579ad8" DESTINATION="ID_917795868" ENDARROW="Default" ENDINCLINATION="-206;8;" ID="Arrow_ID_1287643203" SOURCE="ID_367226311" STARTARROW="None" STARTINCLINATION="292;0;"/>
<node COLOR="#338800" CREATED="1689355095725" ID="ID_917795868" MODIFIED="1689555145374" TEXT="exponentielles moving-average: ein N-tel vom gemittelten Wert subtrahieren">
<linktarget COLOR="#579ad8" DESTINATION="ID_917795868" ENDARROW="Default" ENDINCLINATION="-209;8;" ID="Arrow_ID_1287643203" SOURCE="ID_367226311" STARTARROW="None" STARTINCLINATION="292;0;"/>
<icon BUILTIN="button_ok"/>
</node>
</node>
<node CREATED="1689355137015" ID="ID_864577627" MODIFIED="1689355148010" TEXT="Konsequenz...">
@ -80202,6 +80203,11 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</p>
</body>
</html></richcontent>
<node COLOR="#435e98" CREATED="1689555157106" ID="ID_758511598" MODIFIED="1689555196669" TEXT="zun&#xe4;chst &#xfc;berniimmt INITIAL_EPOCH_STEP diese Rolle"/>
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1689555181934" ID="ID_900092388" MODIFIED="1689555228891" TEXT="sp&#xe4;ter ein persistentes TimingObservable">
<arrowlink COLOR="#8d8fd3" DESTINATION="ID_123055064" ENDARROW="Default" ENDINCLINATION="606;0;" ID="Arrow_ID_1147910894" STARTARROW="None" STARTINCLINATION="216;0;"/>
<icon BUILTIN="hourglass"/>
</node>
</node>
</node>
</node>
@ -80229,12 +80235,21 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<icon BUILTIN="flag-yellow"/>
<node CREATED="1689356233875" ID="ID_1117159632" MODIFIED="1689356241731" TEXT="das wirkt mit einer gewissen Verz&#xf6;gerung"/>
<node CREATED="1689356263015" ID="ID_367226311" MODIFIED="1689356314144" TEXT="hier wird exponential-MA angewendet">
<arrowlink COLOR="#579ad8" DESTINATION="ID_917795868" ENDARROW="Default" ENDINCLINATION="-206;8;" ID="Arrow_ID_1287643203" STARTARROW="None" STARTINCLINATION="292;0;"/>
<arrowlink COLOR="#579ad8" DESTINATION="ID_917795868" ENDARROW="Default" ENDINCLINATION="-209;8;" ID="Arrow_ID_1287643203" STARTARROW="None" STARTINCLINATION="292;0;"/>
</node>
<node CREATED="1689356572198" ID="ID_1540220100" MODIFIED="1689356660985" TEXT="&#xbb;Sollwert&#xab; &#x27f9; als Signal einspeisen">
<node CREATED="1689356669065" ID="ID_492493887" MODIFIED="1689356705152" TEXT="&#xbb;Sollwert&#xab; &#x2261; die Epochen-L&#xe4;nge, die gen&#xfc;gt h&#xe4;tte"/>
<node CREATED="1689356713555" ID="ID_1631540322" MODIFIED="1689356790172" TEXT="angenommen die Deadlines sind gleichverteilt &#x27f9; direkt den F&#xfc;llstand nehmen"/>
<node CREATED="1689356905327" ID="ID_1889066607" MODIFIED="1689357557781" TEXT="&#x27f9; aktuelle L&#xe4;nge / F&#xfc;llstand-Faktor"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1689555058905" ID="ID_241155664" MODIFIED="1689555109664" TEXT="Problem: aktuelle L&#xe4;nge ermitteln">
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1689555238439" ID="ID_1830307338" MODIFIED="1689555250023" TEXT="wir speichern nur die Deadline">
<icon BUILTIN="info"/>
</node>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1689555251273" ID="ID_860911583" MODIFIED="1689555283933" TEXT="f&#xfc;r die erste Epoche ist der Anfangspunkt nicht bekannt">
<icon BUILTIN="broken-line"/>
</node>
</node>
</node>
<node CREATED="1689356940427" ID="ID_1868278031" MODIFIED="1689356956670" TEXT="damit regelt dieser Mechanismus stets herunter"/>
<node CREATED="1689357043350" ID="ID_1771390424" MODIFIED="1689357058027" TEXT="aber langsamer, da er nur im Tick l&#xe4;uft"/>