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) Lumiera.org
5  2024, Hermann Vosseler <Ichthyostega@web.de>
6 
7  This program is free software; you can redistribute it and/or
8  modify it under the terms of the GNU General Public License as
9  published by the Free Software Foundation; either version 2 of
10  the License, or (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 
21 * *****************************************************/
22 
28 #include "lib/test/run.hpp"
30 #include "lib/incidence-count.hpp"
31 #include "lib/thread.hpp"
32 #include "lib/util.hpp"
33 
34 #include <thread>
35 
36 
37 using util::isLimited;
38 using std::this_thread::sleep_for;
39 using std::chrono_literals::operator ""ms;
40 using std::chrono_literals::operator ""us;
41 using std::chrono::microseconds;
42 
43 
44 namespace lib {
45 namespace test{
46 
47  namespace {
48  inline bool
49  isNumEq (double d1, double d2)
50  {
51  return 0.001 > abs(d1-d2);
52  };
53  }
54 
55 
56 
57  /***************************************************************/
64  : public Test
65  {
66  void
67  run (Arg)
68  {
73  }
74 
75 
76 
79  void
81  {
82  IncidenceCount watch;
83  watch.markEnter();
84  sleep_for (1ms);
85  watch.markLeave();
86  //
87  sleep_for (5ms);
88  //
89  watch.markEnter();
90  sleep_for (1ms);
91  watch.markLeave();
92 
93  double time = watch.calcCumulatedTime();
94  CHECK (time > 1900);
95  CHECK (time < 2500);
96  }
97 
98 
100  void
102  {
103  IncidenceCount watch;
104  watch.expectThreads(1)
105  .expectIncidents(20);
106 
107  watch.markEnter(1);
108  sleep_for (1ms);
109  watch.markEnter(3);
110  sleep_for (2ms);
111  watch.markEnter(2);
112  watch.markLeave(3);
113  sleep_for (1ms);
114  watch.markLeave(1);
115  watch.markEnter(3);
116  sleep_for (3ms);
117  watch.markEnter(1);
118  watch.markLeave(2);
119  sleep_for (1ms);
120  watch.markLeave(3);
121  sleep_for (1ms);
122  watch.markLeave(1);
123 
124  auto stat = watch.evaluate();
125 
126  CHECK (isLimited (15500, stat.cumulatedTime, 17800)); // ≈ 16ms
127  CHECK (isLimited ( 8500, stat.coveredTime, 10000)); // ≈ 9ms
128  CHECK (10== stat.eventCnt);
129  CHECK (5 == stat.activationCnt);
130  CHECK (0 == stat.cntCase(0));
131  CHECK (2 == stat.cntCase(1));
132  CHECK (1 == stat.cntCase(2));
133  CHECK (2 == stat.cntCase(3));
134  CHECK (0 == stat.cntCase(4));
135  CHECK (0 == stat.timeCase(0));
136  CHECK (isLimited ( 5500, stat.timeCase(1), 6800)); // ≈ 6ms
137  CHECK (isLimited ( 3500, stat.timeCase(2), 4500)); // ≈ 4ms
138  CHECK (isLimited ( 5500, stat.timeCase(3), 6800)); // ≈ 6ms
139  CHECK (0 == stat.timeCase(4));
140  CHECK (5 == stat.cntThread(0));
141  CHECK (0 == stat.cntThread(1));
142  CHECK (stat.activeTime == stat.timeThread(0));
143  CHECK (0 == stat.timeThread(1));
144  CHECK (isNumEq (stat.activeTime, stat.coveredTime));
145  CHECK (isNumEq (stat.cumulatedTime , stat.timeCase(1) + stat.timeCase(2) + stat.timeCase(3)));
146  }
147 
148 
149 
151  void
153  {
154  IncidenceCount watch;
155  watch.expectThreads(2)
156  .expectIncidents(2);
157 
158  auto act = [&]{ // two nested activities
159  watch.markEnter();
160  sleep_for (600us);
161  watch.markEnter(2);
162  sleep_for (200us);
163  watch.markLeave(2);
164  watch.markLeave();
165  };
166 
167  auto run_parallel = [&]
168  {
169  ThreadJoinable t1("test-1", act);
170  ThreadJoinable t2("test-2", act);
171  t1.join();
172  t2.join();
173  };
174 
175  double runTime = test::benchmarkTime (run_parallel);
176 
177  // join ensures visibility of all data changes from within threads,
178  // which is a prerequisite for performing the data evaluation safely.
179  auto stat = watch.evaluate();
180 
181  CHECK (runTime > stat.coveredTime);
182  CHECK (stat.coveredTime < stat.cumulatedTime);
183  CHECK (stat.activeTime <= stat.cumulatedTime);
184  CHECK (8 == stat.eventCnt);
185  CHECK (4 == stat.activationCnt);
186  CHECK (2 == stat.cntCase(0));
187  CHECK (0 == stat.cntCase(1));
188  CHECK (2 == stat.cntCase(2));
189  CHECK (0 == stat.cntCase(3));
190  CHECK (2 == stat.cntThread(0));
191  CHECK (2 == stat.cntThread(1));
192  CHECK (0 == stat.cntThread(3));
193  CHECK (isLimited(1, stat.avgConcurrency, 2));
194  CHECK (0 == stat.timeAtConc(0));
195  CHECK (0 < stat.timeAtConc(1));
196  CHECK (0 < stat.timeAtConc(2));
197  CHECK (0 == stat.timeAtConc(3));
198  CHECK (stat.timeAtConc(1) < stat.coveredTime);
199  CHECK (stat.timeAtConc(2) < stat.coveredTime);
200 
201  CHECK (isNumEq (stat.avgConcurrency, (1*stat.timeAtConc(1) + 2*stat.timeAtConc(2)) // average concurrency is a weighted mean
202  / stat.coveredTime)); // of the times spent at each concurrency level
203 
204  CHECK (isNumEq (stat.cumulatedTime , stat.timeCase(0) + stat.timeCase(2))); // cumulated time compounds all cases, including overlap
205  CHECK (isNumEq (stat.activeTime , stat.timeThread(0) + stat.timeThread(1))); // while active time disregards overlapping activities per thread
206  CHECK (isNumEq (stat.coveredTime , stat.timeAtConc(1) + stat.timeAtConc(2))); // the covered time happens at any non-zero concurrency level
207 
208  CHECK (stat.timeCase(2) < stat.timeCase(0)); // Note: case-2 is nested into case-0
209  CHECK (isNumEq (stat.coveredTime , stat.timeCase(0) - stat.timeAtConc(2))); // Thus, case-0 brackets all time, minus the overlapping segment
210  }
211 
212 
213 
215  void
217  {
218  constexpr size_t CONCURR = 16;
219  const size_t REPETITIONS = 100;
220 
221  IncidenceCount watch;
222  watch.expectThreads(CONCURR)
223  .expectIncidents(10000);
224 
225  auto act = [&]{ // two nested activities with random delay
226  uint delay = 100 + rand() % 800;
227  watch.markEnter();
228  sleep_for (microseconds(delay));
229  watch.markEnter(2);
230  sleep_for (microseconds(delay));
231  watch.markLeave(2);
232  watch.markLeave();
233  };
234 
235  // Invoke these two nested activations numerous times in several threads
236  auto [runTime, sum] = test::threadBenchmark<CONCURR> (act, REPETITIONS);
237 
238  CHECK (sum == CONCURR*REPETITIONS); // each invocation contributes +1
239  CHECK (isLimited (900, runTime, 1400)); // delay is 500µs on average
240 
241  // compute statistics over recorded events
242  auto stat = watch.evaluate();
243 
244  // on average two times 500µs per invocation
245  CHECK (isLimited (900*REPETITIONS, stat.coveredTime, 1400*REPETITIONS));
246  CHECK (stat.activeTime > 900 * REPETITIONS*CONCURR);
247  CHECK (stat.activationCnt == 2*REPETITIONS*CONCURR);
248  CHECK (stat.cntCase(0) == REPETITIONS*CONCURR);
249  CHECK (stat.cntCase(1) == 0);
250  CHECK (stat.cntCase(2) == REPETITIONS*CONCURR);
251 
252  CHECK (isLimited(CONCURR/2, stat.avgConcurrency, CONCURR));
253  // if there are enough cores, ∅ concurrency should even be close to CONCURR
254 
255  for (uint i=0; i<CONCURR; ++i)
256  CHECK (isLimited(REPETITIONS*900, stat.timeThread(i), REPETITIONS*1400));
257 
258  CHECK (0 == stat.timeThread(CONCURR)); // there should not be any idle time recorded
259  CHECK (0 == stat.timeAtConc(CONCURR+1)); // there should be never more concurrency than number of threads
260  // most of the time, concurrency should be close to the defined maximum
261  CHECK (isLimited(REPETITIONS*900, stat.timeAtConc(CONCURR), REPETITIONS*1200));
262  }
263  };
264 
265  LAUNCHER (IncidenceCount_test, "unit common");
266 
267 
268 }} // namespace lib::test
269 
Variant of the standard case, requiring to wait and join() on the termination of this thread...
Definition: thread.hpp:676
Definition: run.hpp:49
Functions to perform (multithreaded) timing measurement on a given functor.
Record and evaluate concurrent activations.
Implementation namespace for support and library code.
Simple 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:693
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...