diff --git a/src/lib/incidence-count.hpp b/src/lib/incidence-count.hpp index d2b7a94cd..bf1fded76 100644 --- a/src/lib/incidence-count.hpp +++ b/src/lib/incidence-count.hpp @@ -36,7 +36,7 @@ ** 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. + ** would require a much more elaborate ID management, which is beyond requirement's scope. ** ** @see IncidenceCount_test ** @see vault::gear::TestChainLoad::ScheduleCtx @@ -155,7 +155,7 @@ namespace lib { { REQUIRE (cnt); for (Sequence& s : rec_) - s.reserve (cnt); + s.reserve (2*cnt); return *this; } @@ -173,14 +173,15 @@ namespace lib { { size_t eventCnt{0}; size_t activationCnt{0}; - double cumulatedTime{0}; - double coveredTime{0}; - double avgConcurrency{0}; + double cumulatedTime{0}; ///< aggregated time over all cases + double activeTime{0}; ///< compounded time of thread activity + double coveredTime{0}; ///< overall timespan of observation + double avgConcurrency{0}; ///< amortised concurrency in timespan - vector caseCntr{}; - vector thrdCntr{}; - vector caseTime{}; - vector thrdTime{}; + vector caseCntr{}; ///< counting activations per case + vector thrdCntr{}; ///< counting activations per thread + vector caseTime{}; ///< aggregated time per case + vector thrdTime{}; ///< time of activity per thread vector concTime{}; template @@ -235,12 +236,11 @@ namespace lib { ); int active{0}; - size_t numCases = std::numeric_limits::max()+1; - vector active_case(numCases); + vector active_case; vector active_thrd(numThreads); stat.thrdCntr.resize (numThreads); stat.thrdTime.resize (numThreads); - stat.concTime.resize (numThreads); + stat.concTime.resize (numThreads+1); // also accounting for idle times in range // Integrate over the timeline... // - book the preceding interval length into each affected partial sum @@ -250,6 +250,7 @@ namespace lib { { if (event.caseID >= stat.caseCntr.size()) { + active_case .resize (event.caseID+1); stat.caseCntr.resize (event.caseID+1); stat.caseTime.resize (event.caseID+1); } @@ -258,7 +259,8 @@ namespace lib { for (uint i=0; i < stat.caseCntr.size(); ++i) stat.caseTime[i] += active_case[i] * timeSlice.count(); for (uint i=0; i < numThreads; ++i) - stat.thrdTime[i] += active_thrd[i] * timeSlice.count(); + if (active_thrd[i]) // note: counting activity per thread, without overlapping cases + stat.thrdTime[i] += timeSlice.count(); size_t concurr = explore(active_thrd).filter([](int a){ return 0 < a; }).count(); ENSURE (concurr <= numThreads); stat.avgConcurrency += concurr * timeSlice.count(); // contribution for weighted average @@ -289,6 +291,7 @@ namespace lib { ENSURE (0 < stat.activationCnt); ENSURE (stat.eventCnt % 2 == 0); stat.avgConcurrency /= stat.coveredTime; // time used as weight sum + stat.activeTime = explore(stat.thrdTime).resultSum(); return stat; } diff --git a/tests/library/incidence-count-test.cpp b/tests/library/incidence-count-test.cpp index 6cb20d254..6390a8553 100644 --- a/tests/library/incidence-count-test.cpp +++ b/tests/library/incidence-count-test.cpp @@ -40,12 +40,20 @@ using util::isLimited; using std::this_thread::sleep_for; using std::chrono_literals::operator ""ms; +using std::chrono_literals::operator ""us; using std::chrono::microseconds; namespace lib { namespace test{ + namespace { + inline bool + isNumEq (double d1, double d2) + { + return 0.001 > abs(d1-d2); + }; + } @@ -120,6 +128,7 @@ namespace test{ auto stat = watch.evaluate(); SHOW_EXPR(stat.cumulatedTime); SHOW_EXPR(stat.coveredTime); +SHOW_EXPR(stat.activeTime); SHOW_EXPR(stat.eventCnt); SHOW_EXPR(stat.activationCnt); SHOW_EXPR(stat.cntCase(0)); @@ -136,7 +145,7 @@ SHOW_EXPR(stat.cntThread(0)); SHOW_EXPR(stat.cntThread(1)); SHOW_EXPR(stat.timeThread(0)); SHOW_EXPR(stat.timeThread(1)); - CHECK (isLimited (15500, stat.cumulatedTime, 17500)); // ≈ 16ms + CHECK (isLimited (15500, stat.cumulatedTime, 17800)); // ≈ 16ms CHECK (isLimited ( 8500, stat.coveredTime, 10000)); // ≈ 9ms CHECK (10== stat.eventCnt); CHECK (5 == stat.activationCnt); @@ -152,9 +161,10 @@ SHOW_EXPR(stat.timeThread(1)); CHECK (0 == stat.timeCase(4)); CHECK (5 == stat.cntThread(0)); CHECK (0 == stat.cntThread(1)); - CHECK (stat.cumulatedTime == stat.timeThread(0)); - CHECK (0 == stat.timeThread(1)); - CHECK (1 > abs(stat.cumulatedTime - (stat.timeCase(1) + stat.timeCase(2) + stat.timeCase(3)))); + CHECK (stat.activeTime == stat.timeThread(0)); + CHECK (0 == stat.timeThread(1)); + CHECK (isNumEq (stat.activeTime, stat.coveredTime)); + CHECK (isNumEq (stat.cumulatedTime , stat.timeCase(1) + stat.timeCase(2) + stat.timeCase(3))); } @@ -165,18 +175,16 @@ SHOW_EXPR(stat.timeThread(1)); verify_concurrencyStatistic() { MARK_TEST_FUN - const size_t CONCURR = std::thread::hardware_concurrency(); IncidenceCount watch; - watch.expectThreads(CONCURR) - .expectIncidents(5000); + watch.expectThreads(2) + .expectIncidents(2); - auto act = [&]{ // two nested activities with random delay - uint delay = 100 + rand() % 800; + auto act = [&]{ // two nested activities watch.markEnter(); - sleep_for (microseconds(delay)); + sleep_for (600us); watch.markEnter(2); - sleep_for (microseconds(delay)); + sleep_for (200us); watch.markLeave(2); watch.markLeave(); }; @@ -196,6 +204,7 @@ SHOW_EXPR(stat.timeThread(1)); auto stat = watch.evaluate(); SHOW_EXPR(runTime) SHOW_EXPR(stat.cumulatedTime); +SHOW_EXPR(stat.activeTime); SHOW_EXPR(stat.coveredTime); SHOW_EXPR(stat.eventCnt); SHOW_EXPR(stat.activationCnt); @@ -217,8 +226,10 @@ SHOW_EXPR(stat.avgConcurrency); SHOW_EXPR(stat.timeAtConc(0)); SHOW_EXPR(stat.timeAtConc(1)); SHOW_EXPR(stat.timeAtConc(2)); +SHOW_EXPR(stat.timeAtConc(3)); CHECK (runTime > stat.coveredTime); CHECK (stat.coveredTime < stat.cumulatedTime); + CHECK (stat.activeTime <= stat.cumulatedTime); CHECK (8 == stat.eventCnt); CHECK (4 == stat.activationCnt); CHECK (2 == stat.cntCase(0)); @@ -228,18 +239,19 @@ SHOW_EXPR(stat.timeAtConc(2)); CHECK (2 == stat.cntThread(0)); CHECK (2 == stat.cntThread(1)); CHECK (0 == stat.cntThread(3)); - CHECK (isLimited(0, stat.avgConcurrency, 2)); - CHECK (stat.timeAtConc(0) == 0.0); + CHECK (isLimited(1, stat.avgConcurrency, 2)); + CHECK (0 == stat.timeAtConc(0)); + CHECK (0 < stat.timeAtConc(1)); + CHECK (0 < stat.timeAtConc(2)); + CHECK (0 == stat.timeAtConc(3)); CHECK (stat.timeAtConc(1) < stat.coveredTime); CHECK (stat.timeAtConc(2) < stat.coveredTime); - auto isNumEq = [](double d1, double d2){ return 0,001 > abs(d1-d2); }; - CHECK (isNumEq (stat.avgConcurrency, (1*stat.timeAtConc(1) + 2*stat.timeAtConc(2)) // average concurrency is a weighted mean / stat.coveredTime)); // of the times spent at each concurrency level - CHECK (isNumEq (stat.cumulatedTime , stat.timeThread(0) + stat.timeThread(1))); // cumulated time is spent in both threads - CHECK (isNumEq (stat.cumulatedTime , stat.timeCase(0) + stat.timeCase(2))); // and likewise in all cases together + CHECK (isNumEq (stat.cumulatedTime , stat.timeCase(0) + stat.timeCase(2))); // cumulated time compounds all cases, including overlap + CHECK (isNumEq (stat.activeTime , stat.timeThread(0) + stat.timeThread(1))); // while active time disregards overlapping activities per thread CHECK (isNumEq (stat.coveredTime , stat.timeAtConc(1) + stat.timeAtConc(2))); // the covered time happens at any non-zero concurrency level CHECK (stat.timeCase(2) < stat.timeCase(0)); // Note: case-2 is nested into case-0 @@ -253,7 +265,23 @@ SHOW_EXPR(stat.timeAtConc(2)); void perform_multithreadStressTest() { - UNIMPLEMENTED("verify thread-safe operation under pressure"); + MARK_TEST_FUN + const size_t CONCURR = std::thread::hardware_concurrency(); + + IncidenceCount watch; + watch.expectThreads(CONCURR) + .expectIncidents(5000); + + auto act = [&]{ // two nested activities with random delay + uint delay = 100 + rand() % 800; + watch.markEnter(); + sleep_for (microseconds(delay)); + watch.markEnter(2); + sleep_for (microseconds(delay)); + watch.markLeave(2); + watch.markLeave(); + }; + } }; diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index d8bb068f6..12c9d6740 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -111085,6 +111085,18 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + + + + + +