From cb2ee9466b5436ed4c059b76b06bcf9efe182f6d Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 15 Jul 2023 18:54:59 +0200 Subject: [PATCH] Block-Flow: add diagnostics and define further expectations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - fix a bug in IterExplorer: when iterating a »state core« directly, the helper CoreYield passed the detected type through ValueTypeBindings. This is logically wrong, because we never want to pick up some typedefs, rather we always want to use the type directly returned from CORE::yield() Here the iterator returns an Epoch&, which itself is again iterable (it inherits from std::array). However, it is clear that we must not descent into such a "flatMap" style recursive expansion - draft a simple scheme how to regulate Epoch lengths dynamically - add diagnostics to pinpoint a given Activity and find out into which Epoch it has been allocated; used to cover the allocator behaviour --- src/lib/iter-explorer.hpp | 11 +-- src/vault/gear/block-flow.hpp | 68 +++++++++++++++++- tests/vault/gear/block-flow-test.cpp | 56 ++++++++++----- wiki/thinkPad.ichthyo.mm | 103 ++++++++++++++++++++++++++- 4 files changed, 213 insertions(+), 25 deletions(-) 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
+ + + + + +