]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | [/ |
2 | / Copyright (c) 2009-20010 Vicente J. Botet Escriba | |
3 | / | |
4 | / Distributed under the Boost Software License, Version 1.0. (See accompanying | |
5 | / file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
6 | /] | |
7 | ||
8 | ||
9 | [/================] | |
10 | [/section Tutorial] | |
11 | [/================] | |
12 | ||
13 | [/=================================] | |
14 | [section Stopwatches and Stopclocks] | |
15 | [/=================================] | |
16 | ||
17 | At the user level, the main use case of measuring the elapsed time is to report these measures on the display. For example | |
18 | ||
19 | using namespace boost::chrono; | |
20 | ||
21 | int f1(long j) { | |
22 | strict_stopwatch<> sw; | |
23 | ||
24 | for ( long i = 0; i < j; ++i ) | |
25 | std::sqrt( 123.456L ); // burn some time | |
26 | ||
27 | std::cout << sw.elapsed() << std::endl; | |
28 | return 0; | |
29 | } | |
30 | int main() { | |
31 | f1(100000); | |
32 | f1(200000); | |
33 | f1(300000); | |
34 | return 0; | |
35 | } | |
36 | ||
37 | Could produce the following output | |
38 | ||
39 | 0.006s | |
40 | 0.011s | |
41 | 0.017s | |
42 | ||
43 | __stopwatch is a template class with a Clock as template parameter. The default __Clock is the __high_resolution_clock. | |
44 | ||
45 | We can replace the lines | |
46 | ||
47 | strict_stopwatch <> sw; | |
48 | ||
49 | ... | |
50 | std::cout << sw.elapsed() << std::endl; | |
51 | ||
52 | using the __stopwatch_reporter`<>` class. This class provides a run time reporting package that can be invoked in a single line of code to report the usage of a __Clock. For example | |
53 | ||
54 | using namespace boost::chrono; | |
55 | ||
56 | int f1(long j) { | |
57 | __stopwatch_reporter<__strict_stopwatch <> > _; | |
58 | ||
59 | for ( long i = 0; i < j; ++i ) | |
60 | std::sqrt( 123.456L ); // burn some time | |
61 | ||
62 | return 0; | |
63 | } | |
64 | int main() { | |
65 | f1(100000); | |
66 | f1(200000); | |
67 | f1(300000); | |
68 | return 0; | |
69 | } | |
70 | ||
71 | Which produce the same kind of output. | |
72 | ||
73 | __stopwatch_reporter is a template class with a __Stopwatch and a __Formatter as template parameter. | |
74 | ||
75 | We can use even a shorter line using the __stopclock class, which allows to replace | |
76 | ||
77 | __stopwatch_reporter<__strict_stopwatch<> > _; | |
78 | ||
79 | by | |
80 | ||
81 | __strict_stopclock<> _; | |
82 | ||
83 | [section:accumulators Stopwatches accumulation and statistics] | |
84 | ||
85 | The preceding stopwatch manage only with a measure. It is also interesting to have an statisitical view of these times, | |
86 | for example the sum, min, max and mean. __laps_stopwatch`<>` associates an accumulator with a __stopwatch__, so we are able to retrieve any statistical feature Boost.Accumulator provides. | |
87 | ||
88 | For example | |
89 | ||
90 | using namespace boost::chrono; | |
91 | ||
92 | int f1(long j) { | |
93 | static __stopwatch_reporter<__laps_stopwatch<> > sw; | |
94 | __stopwatch_reporter<__laps_stopwatch<> >::scoped_run _(sw); | |
95 | ||
96 | for ( long i = 0; i < j; ++i ) | |
97 | std::sqrt( 123.456L ); // burn some time | |
98 | ||
99 | return 0; | |
100 | } | |
101 | int main() { | |
102 | f1(100000); | |
103 | f1(200000); | |
104 | f1(300000); | |
105 | return 0; | |
106 | } | |
107 | ||
108 | Will produce the following output | |
109 | ||
110 | 3 times, sum=0.034s, min=0.006s, max=0.017s, mean=0.011s | |
111 | ||
112 | ||
113 | [endsect] | |
114 | ||
115 | ||
116 | ||
117 | [section:function How can I prefix each report with `BOOST_CURRENT_FUNCTION` function signature?] | |
118 | ||
119 | You will need to give a specific format to your __stopclock. You just need to concatenate your specific pattern to the default_format of the formatter. | |
120 | ||
121 | For example, for a __stopclock_accumulator the default formatter is __stopwatch_accumulator_formatter, you will need to do something like: | |
122 | ||
123 | static __stopclock_accumulator<> acc( | |
124 | std::string(BOOST_CURRENT_FUNCTION) + ": " | |
125 | + __stopwatch_accumulator_formatter::default_format() | |
126 | ); | |
127 | __stopclock_accumulator<>::scoped_run _(acc); | |
128 | ||
129 | ||
130 | Some of you will say that this is too long to type just to get the a report. You can of course define your own macro as | |
131 | ||
132 | #define REPORT_FUNCTION_ACCUMULATED_LIFETIME\ | |
133 | static boost::stopwatches::__stopclock_accumulator<> \ | |
134 | BOOST_JOIN(_accumulator_, __LINE__)_ | |
135 | ( std::string(BOOST_CURRENT_FUNCTION) + ": " + \ | |
136 | boost::stopwatches::__stopwatch_accumulator_formatter::default_format() \ | |
137 | ); \ | |
138 | boost::stopwatches::__stopclock_accumulator<>::scoped_run \ | |
139 | BOOST_JOIN(_accumulator_run_, __LINE__) \ | |
140 | (BOOST_JOIN(_accumulator_, __LINE__)) | |
141 | ||
142 | ||
143 | With this macro you will just have to write | |
144 | ||
145 | void foo() | |
146 | { | |
147 | REPORT_FUNCTION_ACCUMULATED_LIFETIME() ; | |
148 | boost::this_thread::sleep(boost::posix_time::milliseconds(100)); | |
149 | // ... | |
150 | } | |
151 | ||
152 | [endsect] | |
153 | ||
154 | [section:file_line How can I prefix each report with `__FILE__[__LINE__]` pattern?] | |
155 | ||
156 | When you want to prefix with the `__FILE__[__LINE__]` pattern you can follow the same technique as described below: | |
157 | ||
158 | #define REPORT_LINE_ACCUMULATED_LIFETIME \ | |
159 | static __stopclock_accumulator<> | |
160 | BOOST_JOIN(_accumulator_, __LINE__) \ | |
161 | ( std::string(__FILE__) + "[" + BOOST_STRINGIZE(__LINE__) + "] " | |
162 | + __stopwatch_accumulator_formatter::default_format() \ | |
163 | ); \ | |
164 | __stopclock_accumulator<>::scoped_run \ | |
165 | BOOST_JOIN(_accumulator_run_, __LINE__) | |
166 | (BOOST_JOIN(_accumulator_, __LINE__)) | |
167 | ||
168 | Now you can mix fcntion and line reports as follows | |
169 | ||
170 | void foo() | |
171 | { | |
172 | REPORT_FUNCTION_ACCUMULATED_LIFETIME; | |
173 | boost::this_thread::sleep(boost::posix_time::milliseconds(100)); | |
174 | { | |
175 | REPORT_LINE_ACCUMULATED_LIFETIME; | |
176 | boost::this_thread::sleep(boost::posix_time::milliseconds(200)); | |
177 | } | |
178 | ||
179 | } | |
180 | ||
181 | [endsect] | |
182 | ||
183 | [section:non_static_acc Can I use an stopclock accumulator which is not static?] | |
184 | ||
185 | The typical example of stopclock_accumulator is to get statistical measures of the time a function takes for each one of its calls. | |
186 | You can also use __stopclock_accumulator to get statistical measures of the time a given loop takes for each one of its laps. | |
187 | ||
188 | __stopclock_accumulator<> acc( | |
189 | std::string(__FILE__) + "[" + BOOST_STRINGIZE(__LINE__) + "] " | |
190 | + __stopwatch_accumulator_formatter::default_format() | |
191 | ); | |
192 | for (int i=0; i<N; i++) { | |
193 | __stopclock_accumulator<>::scoped_run _(acc); | |
194 | // ... | |
195 | } | |
196 | ||
197 | ||
198 | [endsect] | |
199 | ||
200 | [section:suspend How can I suspend a stopwatch?] | |
201 | ||
202 | #include <boost/stopwatches/stopwatch.hpp> | |
203 | #include <cmath> | |
204 | #include <boost/thread.hpp> | |
205 | ||
206 | ||
207 | using namespace boost::stopwatches; | |
208 | double res; | |
209 | void f1(long j) | |
210 | { | |
211 | __stopwatch_reporter<__stopwatch__<> >:: _(BOOST_STOPWATCHES_STOPWATCH_FUNCTION_FORMAT); | |
212 | for (long i =0; i< j; i+=1) | |
213 | res+=std::sqrt( res+123.456L+i ); // burn some time | |
214 | __stopwatch_reporter<__stopwatch__<> >::scoped_suspend s(_); | |
215 | boost::this_thread::sleep(boost::posix_time::milliseconds(200)); | |
216 | } | |
217 | ||
218 | [endsect] | |
219 | ||
220 | [section:stats How to get specific statistics from stopwatches accumulator?] | |
221 | ||
222 | There are two use cases that coul need to change the statistics associated to a stopwatches accumulator: | |
223 | ||
224 | # We want to reduce the default reporting and we preffer to adapt the statistics to the reporting | |
225 | # We want to report other statistics of the samples | |
226 | ||
227 | For the first case we just need to change the accumulator_set and the format we want to get. Imagin we want to get only the count, sam and mean statistics, no need to calculate the min neither the max. | |
228 | ||
229 | using namespace boost::accumulators; | |
230 | ||
231 | typedef __stopwatch_reporter<__laps_stopwatch<__process_real_cpu_clock__, | |
232 | accumulator_set<__process_real_cpu_clock__::rep, | |
233 | features< | |
234 | tag::count, | |
235 | tag::sum, | |
236 | tag::mean | |
237 | > | |
238 | > | |
239 | > my_stopwatch_accumulator_reporter; | |
240 | ||
241 | int f1(long j) | |
242 | { | |
243 | static my_stopwatch_accumulator_reporter acc("%c times, sum=%ss, mean=%as\n"); | |
244 | my_stopwatch_accumulator_reporter::scoped_run _(acc); | |
245 | ||
246 | for ( long i = 0; i < j; ++i ) | |
247 | std::sqrt( 123.456L ); // burn some time | |
248 | ||
249 | return 0; | |
250 | } | |
251 | ||
252 | But what would hapend if we haven't forced the format: | |
253 | ||
254 | static my_stopwatch_accumulator_reporter acc; | |
255 | my_stopwatch_accumulator_reporter::scoped_run _(acc); | |
256 | ||
257 | Unfortunately there is no error at compile time. Fortunately, the run-time execution is not undefined and will return 0 for the missing statistics. | |
258 | ||
259 | ||
260 | [endsect] | |
261 | ||
262 | [section Formatting] | |
263 | ||
264 | [section:other How can I make a specific formatter when the default do not satisfy my expectations] | |
265 | ||
266 | Imagine then that we want to report the `tag::variance(lazy)`. We will need to include the specific accumulator file | |
267 | ||
268 | ... | |
269 | #include <boost/accumulators/statistics/variance.hpp> | |
270 | ... | |
271 | typedef __stopwatch_reporter<__laps_stopwatch<__process_real_cpu_clock__, | |
272 | accumulator_set<__process_real_cpu_clock__::rep, | |
273 | features< | |
274 | tag::count, | |
275 | tag::sum, | |
276 | tag::mean, | |
277 | tag::variance(lazy) | |
278 | > | |
279 | > | |
280 | > my_stopwatch_accumulator_reporter; | |
281 | ||
282 | But what happens if we add new statistics to the accumulator_set that are not taken in account by the default formatter? These statistics will simply be ignored. So we will need to define our own accumulator formatter. | |
283 | ||
284 | typedef __stopwatch_reporter<__laps_stopwatch<__process_real_cpu_clock__, | |
285 | accumulator_set<__process_real_cpu_clock__::rep, | |
286 | features< | |
287 | tag::count, | |
288 | tag::sum, | |
289 | tag::mean, | |
290 | tag::variance(lazy) | |
291 | > | |
292 | >, | |
293 | my_stopwatch_accumulator_formatter | |
294 | > my_stopwatch_accumulator_reporter; | |
295 | ||
296 | Next follow the definition of a formatter taking care of count, sum, mean and variance | |
297 | ||
298 | class my_stopwatch_accumulator_formatter { | |
299 | public: | |
300 | typedef std::string string_type; | |
301 | typedef char char_type; | |
302 | typedef std::ostream ostream_type; | |
303 | ||
304 | static ostream_type & default_os() {return std::cout;} | |
305 | static const char_type* default_format() { | |
306 | return "%c times, sum=%ss, mean=%as, variance=%vs\n"; | |
307 | } | |
308 | static int default_places() { return 3; } | |
309 | ||
310 | template <class Stopwatch > | |
311 | static void show_time( Stopwatch & stopwatch_, const char_type* format, | |
312 | int places, ostream_type & os, system::error_code & ec) | |
313 | { | |
314 | typedef typename Stopwatch::duration duration_t; | |
315 | typename Stopwatch::accumulator accumulator& acc = stopwatch_.accumulated(); | |
316 | ||
317 | boost::io::ios_flags_saver ifs( os ); | |
318 | os.setf( std::ios_base::fixed, std::ios_base::floatfield ); | |
319 | boost::io::ios_precision_saver ips( os ); | |
320 | os.precision( places ); | |
321 | ||
322 | for ( ; *format; ++format ) { | |
323 | if ( *format != '%' || !*(format+1) || !std::strchr("acsv", *(format+1)) ) { | |
324 | os << *format; | |
325 | } else { | |
326 | ++format; | |
327 | switch ( *format ) { | |
328 | case 's': | |
329 | os << boost::chrono::duration<double>( | |
330 | duration_t(accumulators::sum(acc))).count(); | |
331 | break; | |
332 | case 'a': | |
333 | os << (accumulators::count(acc)>0) | |
334 | ? boost::chrono::__duration__<double>(duration_t( | |
335 | duration_t::rep(accumulators::mean(acc)))).count() | |
336 | : 0; | |
337 | break; | |
338 | case 'c': | |
339 | os << accumulators::count(acc); | |
340 | break; | |
341 | case 'v': | |
342 | os << (accumulators::count(acc)>0) | |
343 | ? boost::chrono::__duration__<double>(duration_t( | |
344 | duration_t::rep(accumulators::variance(acc)))).count() | |
345 | : 0; | |
346 | break; | |
347 | default: | |
348 | assert(0 && "my_stopwatch_accumulator_formatter internal logic error"); | |
349 | } | |
350 | } | |
351 | } | |
352 | } | |
353 | }; | |
354 | ||
355 | ||
356 | ||
357 | [endsect] | |
358 | ||
359 | ||
360 | [endsect] | |
361 | [endsect] | |
362 | ||
363 | [/===============] | |
364 | [/section Examples] | |
365 | [/===============] | |
366 | ||
367 | ||
368 | ||
369 |