Lumiera  0.pre.03
»edit your freedom«
incidence-count-test.cpp
Go to the documentation of this file.
1 /*
2  IncidenceCount(Test) - observe and evaluate concurrent activations
3 
4  Copyright (C)
5  2024, Hermann Vosseler <Ichthyostega@web.de>
6 
7   **Lumiera** is free software; you can redistribute it and/or modify it
8   under the terms of the GNU General Public License as published by the
9   Free Software Foundation; either version 2 of the License, or (at your
10   option) any later version. See the file COPYING for further details.
11 
12 * *****************************************************************/
13 
19 #include "lib/test/run.hpp"
21 #include "lib/incidence-count.hpp"
22 #include "lib/thread.hpp"
23 #include "lib/util.hpp"
24 
25 #include <thread>
26 
27 
28 using util::isLimited;
29 using std::this_thread::sleep_for;
30 using std::chrono_literals::operator ""ms;
31 using std::chrono_literals::operator ""us;
32 using std::chrono::microseconds;
33 
34 
35 namespace lib {
36 namespace test{
37 
38  namespace {
39  inline bool
40  isNumEq (double d1, double d2)
41  {
42  return 0.001 > abs(d1-d2);
43  };
44  }
45 
46 
47 
48  /***************************************************************/
55  : public Test
56  {
57  void
58  run (Arg)
59  {
64  }
65 
66 
67 
70  void
72  {
73  IncidenceCount watch;
74  watch.markEnter();
75  sleep_for (1ms);
76  watch.markLeave();
77  //
78  sleep_for (5ms);
79  //
80  watch.markEnter();
81  sleep_for (1ms);
82  watch.markLeave();
83 
84  double time = watch.calcCumulatedTime();
85  CHECK (time > 1900);
86  CHECK (time < 2500);
87  }
88 
89 
91  void
93  {
94  IncidenceCount watch;
95  watch.expectThreads(1)
96  .expectIncidents(20);
97 
98  watch.markEnter(1);
99  sleep_for (1ms);
100  watch.markEnter(3);
101  sleep_for (2ms);
102  watch.markEnter(2);
103  watch.markLeave(3);
104  sleep_for (1ms);
105  watch.markLeave(1);
106  watch.markEnter(3);
107  sleep_for (3ms);
108  watch.markEnter(1);
109  watch.markLeave(2);
110  sleep_for (1ms);
111  watch.markLeave(3);
112  sleep_for (1ms);
113  watch.markLeave(1);
114 
115  auto stat = watch.evaluate();
116 
117  CHECK (isLimited (15500, stat.cumulatedTime, 17800)); // ≈ 16ms
118  CHECK (isLimited ( 8500, stat.coveredTime, 10000)); // ≈ 9ms
119  CHECK (10== stat.eventCnt);
120  CHECK (5 == stat.activationCnt);
121  CHECK (0 == stat.cntCase(0));
122  CHECK (2 == stat.cntCase(1));
123  CHECK (1 == stat.cntCase(2));
124  CHECK (2 == stat.cntCase(3));
125  CHECK (0 == stat.cntCase(4));
126  CHECK (0 == stat.timeCase(0));
127  CHECK (isLimited ( 5500, stat.timeCase(1), 6800)); // ≈ 6ms
128  CHECK (isLimited ( 3500, stat.timeCase(2), 4500)); // ≈ 4ms
129  CHECK (isLimited ( 5500, stat.timeCase(3), 6800)); // ≈ 6ms
130  CHECK (0 == stat.timeCase(4));
131  CHECK (5 == stat.cntThread(0));
132  CHECK (0 == stat.cntThread(1));
133  CHECK (stat.activeTime == stat.timeThread(0));
134  CHECK (0 == stat.timeThread(1));
135  CHECK (isNumEq (stat.activeTime, stat.coveredTime));
136  CHECK (isNumEq (stat.cumulatedTime , stat.timeCase(1) + stat.timeCase(2) + stat.timeCase(3)));
137  }
138 
139 
140 
142  void
144  {
145  IncidenceCount watch;
146  watch.expectThreads(2)
147  .expectIncidents(2);
148 
149  auto act = [&]{ // two nested activities
150  watch.markEnter();
151  sleep_for (600us);
152  watch.markEnter(2);
153  sleep_for (200us);
154  watch.markLeave(2);
155  watch.markLeave();
156  };
157 
158  auto run_parallel = [&]
159  {
160  ThreadJoinable t1("test-1", act);
161  ThreadJoinable t2("test-2", act);
162  t1.join();
163  t2.join();
164  };
165 
166  double runTime = test::benchmarkTime (run_parallel);
167 
168  // join ensures visibility of all data changes from within threads,
169  // which is a prerequisite for performing the data evaluation safely.
170  auto stat = watch.evaluate();
171 
172  CHECK (runTime > stat.coveredTime);
173  CHECK (stat.coveredTime < stat.cumulatedTime);
174  CHECK (stat.activeTime <= stat.cumulatedTime);
175  CHECK (8 == stat.eventCnt);
176  CHECK (4 == stat.activationCnt);
177  CHECK (2 == stat.cntCase(0));
178  CHECK (0 == stat.cntCase(1));
179  CHECK (2 == stat.cntCase(2));
180  CHECK (0 == stat.cntCase(3));
181  CHECK (2 == stat.cntThread(0));
182  CHECK (2 == stat.cntThread(1));
183  CHECK (0 == stat.cntThread(3));
184  CHECK (isLimited(1, stat.avgConcurrency, 2));
185  CHECK (0 == stat.timeAtConc(0));
186  CHECK (0 < stat.timeAtConc(1));
187  CHECK (0 < stat.timeAtConc(2));
188  CHECK (0 == stat.timeAtConc(3));
189  CHECK (stat.timeAtConc(1) < stat.coveredTime);
190  CHECK (stat.timeAtConc(2) < stat.coveredTime);
191 
192  CHECK (isNumEq (stat.avgConcurrency, (1*stat.timeAtConc(1) + 2*stat.timeAtConc(2)) // average concurrency is a weighted mean
193  / stat.coveredTime)); // of the times spent at each concurrency level
194 
195  CHECK (isNumEq (stat.cumulatedTime , stat.timeCase(0) + stat.timeCase(2))); // cumulated time compounds all cases, including overlap
196  CHECK (isNumEq (stat.activeTime , stat.timeThread(0) + stat.timeThread(1))); // while active time disregards overlapping activities per thread
197  CHECK (isNumEq (stat.coveredTime , stat.timeAtConc(1) + stat.timeAtConc(2))); // the covered time happens at any non-zero concurrency level
198 
199  CHECK (stat.timeCase(2) < stat.timeCase(0)); // Note: case-2 is nested into case-0
200  CHECK (isNumEq (stat.coveredTime , stat.timeCase(0) - stat.timeAtConc(2))); // Thus, case-0 brackets all time, minus the overlapping segment
201  }
202 
203 
204 
206  void
208  {
209  constexpr size_t CONCURR = 16;
210  const size_t REPETITIONS = 100;
211 
212  IncidenceCount watch;
213  watch.expectThreads(CONCURR)
214  .expectIncidents(10000);
215 
216  auto act = [&
217  ,gen = makeRandGen()]// local random generator per thread
218  () mutable
219  { // two nested activities with random delay
220  uint delay = 100 + gen.i(800);
221  watch.markEnter();
222  sleep_for (microseconds(delay));
223  watch.markEnter(2);
224  sleep_for (microseconds(delay));
225  watch.markLeave(2);
226  watch.markLeave();
227  };
228 
229  // Invoke these two nested activations numerous times in several threads
230  auto [runTime, sum] = test::threadBenchmark<CONCURR> (act, REPETITIONS);
231 
232  CHECK (sum == CONCURR*REPETITIONS); // each invocation contributes +1
233  CHECK (isLimited (900, runTime, 1400)); // delay is 500µs on average
234 
235  // compute statistics over recorded events
236  auto stat = watch.evaluate();
237 
238  // on average two times 500µs per invocation
239  CHECK (isLimited (900*REPETITIONS, stat.coveredTime, 1400*REPETITIONS));
240  CHECK (stat.activeTime > 900 * REPETITIONS*CONCURR);
241  CHECK (stat.activationCnt == 2*REPETITIONS*CONCURR);
242  CHECK (stat.cntCase(0) == REPETITIONS*CONCURR);
243  CHECK (stat.cntCase(1) == 0);
244  CHECK (stat.cntCase(2) == REPETITIONS*CONCURR);
245 
246  CHECK (isLimited(CONCURR/2, stat.avgConcurrency, CONCURR));
247  // if there are enough cores, ∅ concurrency should even be close to CONCURR
248 
249  for (uint i=0; i<CONCURR; ++i)
250  CHECK (isLimited(REPETITIONS*900, stat.timeThread(i), REPETITIONS*1400));
251 
252  CHECK (0 == stat.timeThread(CONCURR)); // there should not be any idle time recorded
253  CHECK (0 == stat.timeAtConc(CONCURR+1)); // there should be never more concurrency than number of threads
254  // most of the time, concurrency should be close to the defined maximum
255  CHECK (isLimited(REPETITIONS*900, stat.timeAtConc(CONCURR), REPETITIONS*1200));
256  }
257  };
258 
259  LAUNCHER (IncidenceCount_test, "unit common");
260 
261 
262 }} // namespace lib::test
263 
Variant of the standard case, requiring to wait and join() on the termination of this thread...
Definition: thread.hpp:668
Definition: run.hpp:40
Functions to perform (multithreaded) timing measurement on a given functor.
Record and evaluate concurrent activations.
Implementation namespace for support and library code.
Simplistic test class runner.
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
lib::Result< RES > join()
put the caller into a blocking wait until this thread has terminated
Definition: thread.hpp:685
Convenience front-end to simplify and codify basic thread handling.
A recorder for concurrent incidences.
Statistic evaluate()
Visit all data captured thus far, construct an unified timeline and then compute statistics evaluatio...