diff --git a/src/lib/incidence-count.hpp b/src/lib/incidence-count.hpp index 5c2cde713..2529c03b9 100644 --- a/src/lib/incidence-count.hpp +++ b/src/lib/incidence-count.hpp @@ -30,6 +30,14 @@ ** a thread local ID is constructed, thereby incrementing an global atomic counter. ** Statistics evaluation is comprised of integrating and sorting the captured ** event log, followed by a summation pass. + ** + ** # Usage and limitations + ** This helper is intended for tests and one-time usage. Create an instance, + ** launch a test, retrieve the observed statistics, destroy the object. Each + ** separate Threads encountered gets the next consecutive ID. Thus it is *not possible* + ** to have long-living instances or even multiple instances of IncidenceCount; doing so + ** would require a much more elaborate ID management, which is beyond requirement's scope. + ** ** @see IncidenceCount_test ** @see vault::gear::TestChainLoad::ScheduleCtx ** @see SchedulerStress_test @@ -41,17 +49,128 @@ //#include "lib/meta/function.hpp" +#include "lib/nocopy.hpp" //#include +#include +#include +#include +#include +#include namespace lib { // using std::forward; + /** + * A recorder for concurrent incidences. + * Start and end of individual activations are recorded by direct calls, + * automatically detecting the thread identity; for further differentiation, + * an additional `caseID` can be given. Accumulated observations can be + * integrated into a statistics evaluation. + * @warning never operate multiple instances of this helper at the same time + */ class IncidenceCount + : util::NonCopyable { + using TIMING_SCALE = std::micro; // Results are in ยต-sec + using Clock = std::chrono::steady_clock; + + using Instance = decltype(Clock::now()); + using Dur = std::chrono::duration; + + struct Inc + { + Instance when{}; + uint8_t thread :8; + uint8_t caseID :8; + bool isLeave :1; + }; + + using Sequence = std::vector; + using Recording = std::vector; + + Recording rec_; + + std::atomic_uint8_t slotID_{0}; + + /** threadsafe allocation of thread/slotID */ + uint8_t + allocateNextSlot() + { // Note : returning previous value before increment + return slotID_.fetch_add(+1, std::memory_order_relaxed); + } + + uint8_t + getMySlot() + { + thread_local uint8_t threadID{allocateNextSlot()}; + ASSERT (threadID < std::numeric_limits::max(), "WOW -- so many threads?"); + return threadID; + } + + Sequence + getMySequence() + { + uint8_t id{getMySlot()}; + if (id >= rec_.size()) + { + rec_.reserve (id); + for (size_t i = rec_.size(); i < id; ++i) + rec_.emplace_back(); + } + return rec_[id]; + } + + public: + IncidenceCount() = default; + + + IncidenceCount& + expectThreads(uint8_t cnt) + { + REQUIRE (cnt); + rec_.reserve (cnt); + for ( ; cnt; --cnt) + rec_.emplace_back(); + return *this; + } + + IncidenceCount& + expectIncidents(size_t cnt) + { + REQUIRE (cnt); + for (Sequence& s : rec_) + s.reserve (cnt); + return *this; + } + + + + /* ===== Measurement API ===== */ + + void + markEnter(uint8_t caseID =0) + { + UNIMPLEMENTED ("Incidence measurement"); + } + + void + markLeave(uint8_t caseID =0) + { + UNIMPLEMENTED ("Incidence measurement"); + } + + + /* ===== Evaluations ===== */ + + double + calcCumulatedTime() + { + UNIMPLEMENTED ("Evaluation"); + } }; diff --git a/tests/library/incidence-count-test.cpp b/tests/library/incidence-count-test.cpp index c0e5d42f3..2860b96de 100644 --- a/tests/library/incidence-count-test.cpp +++ b/tests/library/incidence-count-test.cpp @@ -26,7 +26,7 @@ #include "lib/test/run.hpp" -#include "lib/format-cout.hpp"//////////////TODO RLY? +#include "lib/test/diagnostic-output.hpp"//////////////TODO RLY? #include "lib/incidence-count.hpp" //#include @@ -35,6 +35,7 @@ //using std::string; using std::this_thread::sleep_for; +using std::chrono_literals::operator ""ms; namespace lib { @@ -66,6 +67,15 @@ namespace test{ void demonstrate_usage() { + IncidenceCount watch; + watch.markEnter(); + sleep_for (1ms); + watch.markLeave(); + + double time = watch.calcCumulatedTime(); +SHOW_EXPR(time) + CHECK (time > 900); + CHECK (time < 1100); } diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 4b454738e..ac0c969cd 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -111003,7 +111003,29 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + + + + + + + + + + +

+ Aus Performance-Gründen möchte ich für jeden separaten Thread eine initial belegte slotID haben, so daß der Thread direkt ohne weiteres Locking mit seinen separaten Daten arbeiten kann. Dafür muß ich für neu auftauchende Threads immer die nächste ID vergeben, brauche also einen atomaren counter. Da aber jeder Thread dann seine ID kennen muß, brauche ich zudem eine thread_local-Variable, in der er sich seinen Slot merken kann. Und damit tut sich ein Dilemma auf: ein übergreifendes Management von Instanzen wird richtig komplex, besonders dann, wenn es auch noch performant sein soll. Daher der KISS-Beschluß ⟹ das gesamte Thema wird auf den User abgewältzt +

+ + +
+
+ + + + +
@@ -111013,23 +111035,23 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - - - + + + + - - + + - - + + - - + + - - + +