| [/ |
| / Copyright (c) 2009-20010 Vicente J. Botet Escriba |
| / |
| / Distributed under the Boost Software License, Version 1.0. (See accompanying |
| / file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
| /] |
| |
| |
| [/================] |
| [/section Tutorial] |
| [/================] |
| |
| [/=================================] |
| [section Stopwatches and Stopclocks] |
| [/=================================] |
| |
| At the user level, the main use case of measuring the elapsed time is to report these measures on the display. For example |
| |
| using namespace boost::chrono; |
| |
| int f1(long j) { |
| strict_stopwatch<> sw; |
| |
| for ( long i = 0; i < j; ++i ) |
| std::sqrt( 123.456L ); // burn some time |
| |
| std::cout << sw.elapsed() << std::endl; |
| return 0; |
| } |
| int main() { |
| f1(100000); |
| f1(200000); |
| f1(300000); |
| return 0; |
| } |
| |
| Could produce the following output |
| |
| 0.006s |
| 0.011s |
| 0.017s |
| |
| __stopwatch is a template class with a Clock as template parameter. The default __Clock is the __high_resolution_clock. |
| |
| We can replace the lines |
| |
| strict_stopwatch <> sw; |
| |
| ... |
| std::cout << sw.elapsed() << std::endl; |
| |
| 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 |
| |
| using namespace boost::chrono; |
| |
| int f1(long j) { |
| __stopwatch_reporter<__strict_stopwatch <> > _; |
| |
| for ( long i = 0; i < j; ++i ) |
| std::sqrt( 123.456L ); // burn some time |
| |
| return 0; |
| } |
| int main() { |
| f1(100000); |
| f1(200000); |
| f1(300000); |
| return 0; |
| } |
| |
| Which produce the same kind of output. |
| |
| __stopwatch_reporter is a template class with a __Stopwatch and a __Formatter as template parameter. |
| |
| We can use even a shorter line using the __stopclock class, which allows to replace |
| |
| __stopwatch_reporter<__strict_stopwatch<> > _; |
| |
| by |
| |
| __strict_stopclock<> _; |
| |
| [section:accumulators Stopwatches accumulation and statistics] |
| |
| The preceding stopwatch manage only with a measure. It is also interesting to have an statisitical view of these times, |
| 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. |
| |
| For example |
| |
| using namespace boost::chrono; |
| |
| int f1(long j) { |
| static __stopwatch_reporter<__laps_stopwatch<> > sw; |
| __stopwatch_reporter<__laps_stopwatch<> >::scoped_run _(sw); |
| |
| for ( long i = 0; i < j; ++i ) |
| std::sqrt( 123.456L ); // burn some time |
| |
| return 0; |
| } |
| int main() { |
| f1(100000); |
| f1(200000); |
| f1(300000); |
| return 0; |
| } |
| |
| Will produce the following output |
| |
| 3 times, sum=0.034s, min=0.006s, max=0.017s, mean=0.011s |
| |
| |
| [endsect] |
| |
| |
| |
| [section:function How can I prefix each report with `BOOST_CURRENT_FUNCTION` function signature?] |
| |
| 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. |
| |
| For example, for a __stopclock_accumulator the default formatter is __stopwatch_accumulator_formatter, you will need to do something like: |
| |
| static __stopclock_accumulator<> acc( |
| std::string(BOOST_CURRENT_FUNCTION) + ": " |
| + __stopwatch_accumulator_formatter::default_format() |
| ); |
| __stopclock_accumulator<>::scoped_run _(acc); |
| |
| |
| 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 |
| |
| #define REPORT_FUNCTION_ACCUMULATED_LIFETIME\ |
| static boost::stopwatches::__stopclock_accumulator<> \ |
| BOOST_JOIN(_accumulator_, __LINE__)_ |
| ( std::string(BOOST_CURRENT_FUNCTION) + ": " + \ |
| boost::stopwatches::__stopwatch_accumulator_formatter::default_format() \ |
| ); \ |
| boost::stopwatches::__stopclock_accumulator<>::scoped_run \ |
| BOOST_JOIN(_accumulator_run_, __LINE__) \ |
| (BOOST_JOIN(_accumulator_, __LINE__)) |
| |
| |
| With this macro you will just have to write |
| |
| void foo() |
| { |
| REPORT_FUNCTION_ACCUMULATED_LIFETIME() ; |
| boost::this_thread::sleep(boost::posix_time::milliseconds(100)); |
| // ... |
| } |
| |
| [endsect] |
| |
| [section:file_line How can I prefix each report with `__FILE__[__LINE__]` pattern?] |
| |
| When you want to prefix with the `__FILE__[__LINE__]` pattern you can follow the same technique as described below: |
| |
| #define REPORT_LINE_ACCUMULATED_LIFETIME \ |
| static __stopclock_accumulator<> |
| BOOST_JOIN(_accumulator_, __LINE__) \ |
| ( std::string(__FILE__) + "[" + BOOST_STRINGIZE(__LINE__) + "] " |
| + __stopwatch_accumulator_formatter::default_format() \ |
| ); \ |
| __stopclock_accumulator<>::scoped_run \ |
| BOOST_JOIN(_accumulator_run_, __LINE__) |
| (BOOST_JOIN(_accumulator_, __LINE__)) |
| |
| Now you can mix fcntion and line reports as follows |
| |
| void foo() |
| { |
| REPORT_FUNCTION_ACCUMULATED_LIFETIME; |
| boost::this_thread::sleep(boost::posix_time::milliseconds(100)); |
| { |
| REPORT_LINE_ACCUMULATED_LIFETIME; |
| boost::this_thread::sleep(boost::posix_time::milliseconds(200)); |
| } |
| |
| } |
| |
| [endsect] |
| |
| [section:non_static_acc Can I use an stopclock accumulator which is not static?] |
| |
| The typical example of stopclock_accumulator is to get statistical measures of the time a function takes for each one of its calls. |
| You can also use __stopclock_accumulator to get statistical measures of the time a given loop takes for each one of its laps. |
| |
| __stopclock_accumulator<> acc( |
| std::string(__FILE__) + "[" + BOOST_STRINGIZE(__LINE__) + "] " |
| + __stopwatch_accumulator_formatter::default_format() |
| ); |
| for (int i=0; i<N; i++) { |
| __stopclock_accumulator<>::scoped_run _(acc); |
| // ... |
| } |
| |
| |
| [endsect] |
| |
| [section:suspend How can I suspend a stopwatch?] |
| |
| #include <boost/stopwatches/stopwatch.hpp> |
| #include <cmath> |
| #include <boost/thread.hpp> |
| |
| |
| using namespace boost::stopwatches; |
| double res; |
| void f1(long j) |
| { |
| __stopwatch_reporter<__stopwatch__<> >:: _(BOOST_STOPWATCHES_STOPWATCH_FUNCTION_FORMAT); |
| for (long i =0; i< j; i+=1) |
| res+=std::sqrt( res+123.456L+i ); // burn some time |
| __stopwatch_reporter<__stopwatch__<> >::scoped_suspend s(_); |
| boost::this_thread::sleep(boost::posix_time::milliseconds(200)); |
| } |
| |
| [endsect] |
| |
| [section:stats How to get specific statistics from stopwatches accumulator?] |
| |
| There are two use cases that coul need to change the statistics associated to a stopwatches accumulator: |
| |
| # We want to reduce the default reporting and we preffer to adapt the statistics to the reporting |
| # We want to report other statistics of the samples |
| |
| 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. |
| |
| using namespace boost::accumulators; |
| |
| typedef __stopwatch_reporter<__laps_stopwatch<__process_real_cpu_clock__, |
| accumulator_set<__process_real_cpu_clock__::rep, |
| features< |
| tag::count, |
| tag::sum, |
| tag::mean |
| > |
| > |
| > my_stopwatch_accumulator_reporter; |
| |
| int f1(long j) |
| { |
| static my_stopwatch_accumulator_reporter acc("%c times, sum=%ss, mean=%as\n"); |
| my_stopwatch_accumulator_reporter::scoped_run _(acc); |
| |
| for ( long i = 0; i < j; ++i ) |
| std::sqrt( 123.456L ); // burn some time |
| |
| return 0; |
| } |
| |
| But what would hapend if we haven't forced the format: |
| |
| static my_stopwatch_accumulator_reporter acc; |
| my_stopwatch_accumulator_reporter::scoped_run _(acc); |
| |
| Unfortunately there is no error at compile time. Fortunately, the run-time execution is not undefined and will return 0 for the missing statistics. |
| |
| |
| [endsect] |
| |
| [section Formatting] |
| |
| [section:other How can I make a specific formatter when the default do not satisfy my expectations] |
| |
| Imagine then that we want to report the `tag::variance(lazy)`. We will need to include the specific accumulator file |
| |
| ... |
| #include <boost/accumulators/statistics/variance.hpp> |
| ... |
| typedef __stopwatch_reporter<__laps_stopwatch<__process_real_cpu_clock__, |
| accumulator_set<__process_real_cpu_clock__::rep, |
| features< |
| tag::count, |
| tag::sum, |
| tag::mean, |
| tag::variance(lazy) |
| > |
| > |
| > my_stopwatch_accumulator_reporter; |
| |
| 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. |
| |
| typedef __stopwatch_reporter<__laps_stopwatch<__process_real_cpu_clock__, |
| accumulator_set<__process_real_cpu_clock__::rep, |
| features< |
| tag::count, |
| tag::sum, |
| tag::mean, |
| tag::variance(lazy) |
| > |
| >, |
| my_stopwatch_accumulator_formatter |
| > my_stopwatch_accumulator_reporter; |
| |
| Next follow the definition of a formatter taking care of count, sum, mean and variance |
| |
| class my_stopwatch_accumulator_formatter { |
| public: |
| typedef std::string string_type; |
| typedef char char_type; |
| typedef std::ostream ostream_type; |
| |
| static ostream_type & default_os() {return std::cout;} |
| static const char_type* default_format() { |
| return "%c times, sum=%ss, mean=%as, variance=%vs\n"; |
| } |
| static int default_places() { return 3; } |
| |
| template <class Stopwatch > |
| static void show_time( Stopwatch & stopwatch_, const char_type* format, |
| int places, ostream_type & os, system::error_code & ec) |
| { |
| typedef typename Stopwatch::duration duration_t; |
| typename Stopwatch::accumulator accumulator& acc = stopwatch_.accumulated(); |
| |
| boost::io::ios_flags_saver ifs( os ); |
| os.setf( std::ios_base::fixed, std::ios_base::floatfield ); |
| boost::io::ios_precision_saver ips( os ); |
| os.precision( places ); |
| |
| for ( ; *format; ++format ) { |
| if ( *format != '%' || !*(format+1) || !std::strchr("acsv", *(format+1)) ) { |
| os << *format; |
| } else { |
| ++format; |
| switch ( *format ) { |
| case 's': |
| os << boost::chrono::duration<double>( |
| duration_t(accumulators::sum(acc))).count(); |
| break; |
| case 'a': |
| os << (accumulators::count(acc)>0) |
| ? boost::chrono::__duration__<double>(duration_t( |
| duration_t::rep(accumulators::mean(acc)))).count() |
| : 0; |
| break; |
| case 'c': |
| os << accumulators::count(acc); |
| break; |
| case 'v': |
| os << (accumulators::count(acc)>0) |
| ? boost::chrono::__duration__<double>(duration_t( |
| duration_t::rep(accumulators::variance(acc)))).count() |
| : 0; |
| break; |
| default: |
| assert(0 && "my_stopwatch_accumulator_formatter internal logic error"); |
| } |
| } |
| } |
| } |
| }; |
| |
| |
| |
| [endsect] |
| |
| |
| [endsect] |
| [endsect] |
| |
| [/===============] |
| [/section Examples] |
| [/===============] |
| |
| |
| |
| |