diff --git a/src/vault/gear/block-flow.hpp b/src/vault/gear/block-flow.hpp
index d709e2329..decdf3e86 100644
--- a/src/vault/gear/block-flow.hpp
+++ b/src/vault/gear/block-flow.hpp
@@ -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);
}
diff --git a/tests/vault/gear/block-flow-test.cpp b/tests/vault/gear/block-flow-test.cpp
index fb326b0ab..12757a9f8 100644
--- a/tests/vault/gear/block-flow-test.cpp
+++ b/tests/vault/gear/block-flow-test.cpp
@@ -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.
diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm
index 49f738c8c..4169c1ec2 100644
--- a/wiki/thinkPad.ichthyo.mm
+++ b/wiki/thinkPad.ichthyo.mm
@@ -79747,8 +79747,7 @@ Date: Thu Apr 20 18:53:17 2023 +0200
Entscheidung: Allokation entgleist nur ausnahmsweise