blob: 9677fe0967a10cb63c4829d1ed0410aead4a1965 [file] [log] [blame]
[/
/ 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]
[/===============]