diff --git a/src/lib/iter-explorer.hpp b/src/lib/iter-explorer.hpp index cb54a74e2..b4bebeffc 100644 --- a/src/lib/iter-explorer.hpp +++ b/src/lib/iter-explorer.hpp @@ -269,9 +269,9 @@ namespace lib { { using Res = remove_reference_t().yield())>; - using value_type = typename meta::ValueTypeBinding::value_type; - using reference = typename meta::ValueTypeBinding::reference; - using pointer = typename meta::ValueTypeBinding::pointer; + using value_type = typename meta::RefTraits::Value; + using reference = typename meta::RefTraits::Reference; + using pointer = typename meta::RefTraits::Pointer; }; @@ -285,8 +285,9 @@ namespace lib { template struct _DecoratorTraits>> { - using SrcVal = typename CoreYield::value_type; - using SrcIter = lib::IterableDecorator>; + using SrcRaw = typename lib::meta::Strip::Type; + using SrcVal = typename CoreYield::value_type; + using SrcIter = lib::IterableDecorator>; }; template diff --git a/src/vault/gear/block-flow.hpp b/src/vault/gear/block-flow.hpp index 42af55823..63df043db 100644 --- a/src/vault/gear/block-flow.hpp +++ b/src/vault/gear/block-flow.hpp @@ -55,6 +55,9 @@ #include "vault/gear/activity.hpp" #include "vault/mem/extent-family.hpp" #include "lib/time/timevalue.hpp" +#include "lib/iter-explorer.hpp" +#include "lib/format-util.hpp" +#include "lib/rational.hpp" #include "lib/nocopy.hpp" #include "lib/util.hpp" @@ -64,6 +67,7 @@ namespace vault{ namespace gear { + using util::Rat; using util::isnil; using lib::time::Time; using lib::time::TimeVar; @@ -80,6 +84,9 @@ namespace gear { const Duration INITIAL_EPOCH_STEP{FRAMES_PER_EPOCH * FrameRate{50}.duration()}; + const Rat OVERFLOW_BOOST_FACTOR = 9_r/10; ///< increase capacity on each Epoch overflow event + const size_t AVERAGE_EPOCHS = 10; ///< moving average len for exponential convergence towards average Epoch fill + /** raw allocator to provide a sequence of Extents to place Activity records */ using Allocator = mem::ExtentFamily; } @@ -218,11 +225,17 @@ namespace gear { { } Duration - currEpochStep() const + getEpochStep() const { return Duration{epochStep_}; } + void + adjustEpochStep (Rat factor) + { + epochStep_ = getEpochStep() * factor; + } + /** Adapted storage-extent iterator, directly exposing Extent& */ using EpochIter = lib::IterableDecorator; @@ -254,6 +267,10 @@ namespace gear { return *new(claimSlot()) Activity {std::forward (args)...}; } + Time currDeadline() const { return epoch_->deadline(); } + bool hasFreeSlot() const { return epoch_->gate().hasFreeSlot(); } + + private: void* claimSlot() ///< EX_SANE @@ -306,6 +323,34 @@ namespace gear { } + /** + * Notify and adjust Epoch capacity as consequence of exhausting an Epoch. + * Whenever some Epoch can not accommodate a required allocation, the allocation + * is placed into subsequent Epoch(s) and then this event is triggered, reducing + * the epochStep_ by #OVERFLOW_BOOST_FACTOR to increase capacity. + */ + void + markEpochOverflow() + { + UNIMPLEMENTED ("adjust size after overflow"); + } + + /** + * On clean-up of past Epochs, the actual fill factor is checked to guess an + * Epoch duration for optimal usage of epoch storage. Assuming that requested + * Activity deadlines are evenly spaced, for a simple heuristic we can just divide + * 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. + */ + void + markEpochUnderflow (Duration actualLen, Rat fillFactor) + { + UNIMPLEMENTED ("adjust size when detecting partially filled Epochs"); + } + private: Epoch& @@ -354,6 +399,27 @@ namespace gear { Time last() { return flow_.lastEpoch().deadline(); } size_t cntEpochs() { return watch(flow_.alloc_).active(); } size_t poolSize() { return watch(flow_.alloc_).size(); } + + /** find out in which Epoch the given Activity was placed */ + TimeValue + find (Activity& someActivity) + { + for (Epoch& epoch : flow_.allEpochs()) + for (Activity& act : epoch) + if (util::isSameObject (act, someActivity)) + return epoch.deadline(); + return Time::NEVER; + } + + /** render deadlines of all currently active Epochs */ + std::string + allEpochs() + { + if (isnil (flow_.alloc_)) return ""; + auto deadlines = lib::explore (flow_.allEpochs()) + .transform([](Epoch& a){ return TimeValue{a.deadline()}; }); + return util::join(deadlines, "|"); + } }; inline FlowDiagnostic diff --git a/tests/vault/gear/block-flow-test.cpp b/tests/vault/gear/block-flow-test.cpp index 1cad97d90..480d44d7a 100644 --- a/tests/vault/gear/block-flow-test.cpp +++ b/tests/vault/gear/block-flow-test.cpp @@ -88,7 +88,7 @@ namespace test { CHECK (tick.verb_ == Activity::TICK); CHECK (1 == watch(bFlow).cntEpochs()); CHECK (watch(bFlow).first() > deadline); - CHECK (watch(bFlow).first() - deadline == bFlow.currEpochStep()); + CHECK (watch(bFlow).first() - deadline == bFlow.getEpochStep()); bFlow.discardBefore (deadline + Time{0,5}); CHECK (0 == watch(bFlow).cntEpochs()); @@ -217,22 +217,22 @@ namespace test { Time t3 = Time{ 0,11}; auto& a1 = bFlow.until(t1).create(); - CHECK (watch(bFlow).allEpochs() == "10:200"_expect); - CHECK (watch(bFlow).find(a1) == "10:200"_expect); + CHECK (watch(bFlow).allEpochs() == "10s200ms"_expect); + CHECK (watch(bFlow).find(a1) == "10s200ms"_expect); auto& a3 = bFlow.until(t3).create(); - CHECK (watch(bFlow).allEpochs() == "10:200|10:400|10:600|10:800|11:00"_expect); - CHECK (watch(bFlow).find(a3) == "11:000"_expect); + CHECK (watch(bFlow).allEpochs() == "10s200ms|10s400ms|10s600ms|10s800ms|11s0ms"_expect); + CHECK (watch(bFlow).find(a3) == "11s0ms"_expect); auto& a2 = bFlow.until(t2).create(); - CHECK (watch(bFlow).allEpochs() == "10:200|10:400|10:600|10:800|11:00"_expect); - CHECK (watch(bFlow).find(a2) == "11:600"_expect); + CHECK (watch(bFlow).allEpochs() == "10s200ms|10s400ms|10s600ms|10s800ms|11s00ms"_expect); + CHECK (watch(bFlow).find(a2) == "11s600ms"_expect); Time t0 = Time{0,5}; auto& a0 = bFlow.until(t0).create(); - CHECK (watch(bFlow).allEpochs() == "10:200|10:400|10:600|10:800|11:00"_expect); - CHECK (watch(bFlow).find(a2) == "10:200"_expect); + CHECK (watch(bFlow).allEpochs() == "10s200ms|10s400ms|10s600ms|10s800ms|11s00ms"_expect); + CHECK (watch(bFlow).find(a0) == "10s200ms"_expect); BlockFlow::AllocatorHandle allocHandle = bFlow.until(Time{300,10}); for (uint i=1; i + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -80056,6 +80084,14 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + + @@ -80065,17 +80101,74 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + + + + + + + + + + + + +
    +
  • + gewichtetes Mittel +
  • +
  • + eigentlich sind es N alte Punkte, aber wir ziehen ja jedesmal einen Punkt ab ⟹ N-1 gemittelte Beiträge +
  • +
+ +
+
+ + + + + + + + + +

+ denn sonst könnte es relativ lang dauern, bis sich das System einreguliert, und während dieser Zeit könnte es bereits in Sättigung gehen, und Regelschwingungen produzieren +

+ +
+
+
+ + + + + + + + + + + + + + + + + + @@ -80187,7 +80280,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -80204,6 +80297,12 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + +