CPU Timers

| Timer Library CPU Timers

Timer Home CPU timers Original timers

## Introduction

Contents
Introduction Using the timers Using `auto_cpu_timer` Using `cpu_timer` Timer accuracy Resolution Other concerns Recommendations Reference `<boost/timer/timer.hpp>` synopsis Default format Typedef `nanosecond_type` Namespace scope functions `format()` Class `cpu_timer` `cpu_timer` constructors, destructor `cpu_timer` observers `cpu_timer` actions Class `auto_cpu_timer` `auto_cpu_timer` constructors `auto_cpu_timer` destructor `auto_cpu_timer` observers `auto_cpu_timer` actions History Acknowledgements

Knowing how long a program takes to execute is useful in both test and production environments. It may also be helpful if such timing information is broken down into wall clock time, CPU time spent by the user, and CPU time spent by the operating system servicing user requests.

Class `cpu_timer` measures wall clock time, user CPU process time, and system CPU process time. Class `auto_cpu_timer` is a refinement of `cpu_timer` that automatically reports the elapsed times when an `auto_cpu_timer` object is destroyed.

## Setup

Boost.Timer is implemented as a separately compiled library, so you must install binaries in a location that can be found by your linker. If you followed the Boost Getting Started instructions, that's already done for you.

## Using the timers

### Using `auto_cpu_timer`

The simplest and most common use is to add the two lines highlighted below to a scope you want to time. See `auto_cpu_timer_example.cpp` for the source code.

>     #include <boost/timer/timer.hpp>
>     #include <cmath>

>     int main()
>     {
>       boost::timer::auto_cpu_timer t;

>       for (long i = 0; i < 100000000; ++i)
>         std::sqrt(123.456L); // burn some time

>       return 0;
>     }

When the `auto_cpu_timer` object is created, it starts timing. When it is destroyed at the end of the scope, its destructor stops the timer and displays timing information on the default output stream, `std::cout`.

The output of this program will look something like this:

`5.713010s wall, 5.709637s user + 0.000000s system = 5.709637s CPU (99.9%)`

In other words, this program ran in `5.713010` seconds as would be measured by a clock on the wall, the operating system charged it for `5.709637` seconds of user CPU time and 0 seconds of system CPU time, the total of these two was `5.709637`, and that represented `99.9` percent of the wall clock time.

The output stream, number of decimal places reported, and reporting format can be controlled by `auto_cpu_timer` constructor arguments. Here is what the output from the above program would look like for several different sets of constructor arguments:

Construction | Output
`t` | `5.713010s wall, 5.709637s user + 0.000000s system = 5.709637s CPU (99.9%)`
`t(std::cerr, 2)` | `5.71s wall, 5.70s user + 0.00s system = 5.70s CPU (99.9%)`
`t(1)` | `5.7s wall, 5.7s user + 0.0s system = 5.7s CPU (99.9%)`
`t(3, "%w seconds\n")` | `5.713 seconds`
`t("%t sec CPU, %w sec real")` | `5.709637 sec CPU, 5.713010 sec real`

The processing of the format string is described here.

### Using `cpu_timer`

The following code creates a checkpoint every 20 CPU seconds:

>     using boost::timer::cpu_timer;
>     using boost::timer::cpu_times;
>     using boost::timer::nanosecond_type;
>     ...
>     nanosecond_type const twenty_seconds(20 * 1000000000LL);
>     nanosecond_type last(0);
>     cpu_timer timer;
>     while (more_transactions)
>     {
>       process_a_transaction();
>       cpu_times const elapsed_times(timer.elapsed());
>       nanosecond_type const elapsed(elapsed_times.system
>         + elapsed_times.user);
>       if (elapsed >= twenty_seconds)
>       {
>         ... create a checkpoint ...
>         last = elapsed;
>       }
>     }

## Timer accuracy

How accurate are these timers?

### Resolution

The resolution of a clock, and thus timers built on that clock, is the minimum period time that can be measured. The program `cpu_timer_info.cpp` measures the resolution of `cpu_timer`.

O/S | Processor | Wall-clock | CPU
Resolution | Comments | User Resolution | System Resolution
Mac OS X Lion | Intel circa 2007 | 2100ns 2200ns | Some variation within a range. | 10000000ns | 10000000ns
Ubuntu Linux 11.4 | Intel circa 2005 | 516ns | Very little variation, typically less than 5ns | 10000000ns | 10000000ns
Windows 7 | Intel Core i7 860 @ 2.9 GHz | 366ns | Some variation, usually in multiples of 366ns | 15600100ns | 15600100ns
Windows 7 | Intel Mobile T7200 @ 2.0 GHz | 2050ns | Much variation. Resolution degrades when processor slows, probably due to known chipset errata. | 15600100ns | 15600100ns
Windows XP | Intel Atom N2800 @ 1.0 GHz | 1437ns | Some variation. | 15625000ns | 15625000ns

### Other concerns

Wall-clock timings are subject to many outside influences, such as the impact of other processes.

> `cpu_timer` and `auto_cpu_timer` obtain Wall-clock timings from Boost.Chrono's `high_resolution_clock`. On Intel compatible CPU's running Windows, Linux, and Mac OS X, this is a "steady clock" [C++11 20.11.3], but may not be steady on other platforms. `cpu_timer_info.cpp` reports whether or not the `high_resolution_clock` is steady on a particular platform.

> Steady clocks are defined by the C++11 standard as clocks for which values never decrease as physical time advances and for which values advance at a steady rate relative to real time. That is, the clock may not be adjusted. Clocks that are steady never run backwards, even when the operating system's clock is reset backwards such as during a daylight saving time transition.

Timings of debug builds are often several times slower than release builds, because compiler optimization is turned off and because libraries often supply very expensive error checks on debug builds.

Synthetic benchmark code may be optimized way, particularly if NDEBUG is defined. It may be necessary to inspect generated code to verify this isn't happening.

### Recommendations

Think about what is important to your application. For a production process, the wall clock time may be what is most important. To study the efficiency of code, total CPU time (user + system) is often a much better measure.

A useful recommendation is to never trust timings unless they are (1) at least 100 times longer than the CPU time resolution, (2) run multiple times, and (3) run on release builds. And results that are too good to be true need to be should be investigated skeptically.

Shared libraries (DLLs and .so's) may incur extra time delays, including expensive disk accesses, the first time a timer or other function is called. If that would be misleading, static linking should be considered.

## Reference

Specifications are given in the style of the C++ standard library (C++11, 17.5.1.4 [structure.specifications]). An additional Overview element may be provided to aid understanding. Overview elements are only informative - actual semantics are given by the other detailed specification elements.

Functions not specified as `noexcept` will throw `std::bad_alloc` exceptions if a memory allocation error occurs. Other errors are reported by time values of -1. [Note: Modern hardware and operating systems have robust clock subsystems, so such errors are unusual if even possible at all. -- end note]

The Timer library meets the same data race avoidance requirements as the C++11 standard library (17.6.5.9 [res.on.data.races]). Shared objects of Timer library types risk undefined behavior unless the user supplies a locking mechanism. See C++11, 17.6.4.10 [res.on.objects], Shared objects and the library.

### `<boost/timer/timer.hpp>` synopsis

>     namespace boost
>     {
>       namespace timer
>       {
>         class cpu_timer;       // wall clock, user, and system timer
>         class auto_cpu_timer;  // automatic report() on destruction

>         typedef boost::int_least64_t nanosecond_type;

>         struct cpu_times
>         {
>           nanosecond_type wall;
>           nanosecond_type user;
>           nanosecond_type system;

>           void clear();
>         };

>         const int           default_places = 6;

>         std::string format(const cpu_times& times, short places, const std::string& format);
>         std::string format(const cpu_times& times, short places = default_places);

>       } // namespace timer
>     } // namespace boost

### Default format

The default format is " %ws wall, %us user + %ss system = %ts CPU (%p%)\n".

### Typedef `nanosecond_type`

The typedef `nanosecond_type` provides an implementation defined type capable of representing nanoseconds. For POSIX and Windows systems, `nanoseconds_type` is `boost::int_least64_t`.

The underlying type is not based on the Boost Date-Time or Chrono library to avoid a dependency on a large library. This design choice may change at some future date.

Although `nanosecond_type` is capable of representing one nanosecond, the actual resolution of common operating system timers may be much lower. For wall clock time on desktop systems circa 2010, resolution is often no better than than one microsecond. For user and system time, typical resolution is 15 milliseconds on Windows and 10 milliseconds on POSIX.

### Struct `cpu_times`

Struct `cpu_times` packages the elapsed wall clock time, user process CPU time, and system process CPU time. See Current time values for definitions of the source of these elapsed times.

    void clear();

> Effects: `wall = user = system = 0LL`.

### Namespace scope functions

    std::string format(const cpu_times& times, short places, const std::string& format);
    std::string format(const cpu_times& times, short places = default_places);

> Overview: Converts `times`'s values to strings representing seconds to `places` decimal places, and inserts them into the return string as controlled by `format`.

> Remarks: For the overload without the `format` argument, the default format is used as `format`.

> Returns: A string that is a copy of `format`, except that any instances of the sequences shown below are replaced by the indicated value. Times are reported in seconds, shown to `std::max(0, std::min(default_places, 9))` decimal places. Percentage is reported to one decimal place. [Note: percentage may exceed 100% due to differences in how operating systems measure various times. --end note]

> Format replacement sequences

> Sequence | Replacement value
> `%w` | `times.wall`
> `%u` | `times.user`
> `%s` | `times.system`
> `%t` | `times.user + times.system`
> `%p` | The percentage of `times.wall` represented by `times.user + times.system`

### Class `cpu_timer`

`cpu_timer` objects measure wall clock elapsed time and process elapsed time charged to the user and system.

Current time values are the current wall clock time, user process time, and system process time as provided by the operating system:

- Wall clock time is time as would be measured by an ordinary wristwatch or clock on the wall.
- User process time is "the CPU time charged for the execution of user instructions of the calling process." See POSIX.
- System process time is "the CPU time charged for execution by the system on behalf of the calling process." See POSIX.

### `cpu_timer` synopsis

        class cpu_timer
        {
        public:

          //  constructor
          cpu_timer() noexcept;

          //  compiler generated; shown for exposition only
         ~cpu_timer() noexcept = default;
          cpu_timer(const cpu_timer&) noexcept = default;
          cpu_timer& operator=(const cpu_timer&) noexcept = default;

          //  observers
          bool         is_stopped() const noexcept;
          cpu_times    elapsed() const noexcept;
          std::string  format(int places, const std::string& format) const;
          std::string  format(int places = default_places) const;

          //  actions
          void         start() noexcept;
          void         stop() noexcept;
          void         resume() noexcept;
        };

### `cpu_timer` constructor

    cpu_timer() noexcept;

> Effects: Constructs an object of type `cpu_timer`. Calls`start()`.

### `cpu_timer` observers

    bool is_stopped() const noexcept;

> Returns: `true` if stop() was the most recent action function called, otherwise `false`.

    cpu_times elapsed() const noexcept;

> Returns: If `is_stopped()`, the accumulated elapsed times as of the previous stop(). Otherwise, the elapsed times accumulated between the most recent call to start() or resume() and the current time values.

    std::string       format(int places, const std::string& format) const;
    std::string       format(int places = default_places) const;

> Overview: Returns a string for the current elapsed time as formatted by the format non-member function.

> Returns: `boost::timer::format(elapsed(), places[, format])`.

### `cpu_timer` actions

    void start() noexcept;

> Effects: Begins accumulating elapsed time as of the current time values.

> Postconditions: `!is_stopped()`.

    void stop() noexcept;

> Effects: If `!is_stopped()`, stops accumulating elapsed time as of the current time values.

> > [Note: This is observable via `elapsed()`. -- end note]

> Postconditions: `is_stopped()`.

    void resume() noexcept;

> Overview: Restarts the timer, accumulating additional elapsed time.

> Effects: If `is_stopped()`, resumes accumulating additional elapsed time, as of the current time values. Otherwise, no effect.

### Class `auto_cpu_timer`

Class `auto_cpu_timer` adds a `report()` function to `class cpu_timer`, and automatically calls `report()` on destruction.

### `auto_cpu_timer` synopsis

        class auto_cpu_timer : public cpu_timer
        {
        public:
          explicit auto_cpu_timer(short places = default_places);
                   auto_cpu_timer(short places, const std::string& format);
          explicit auto_cpu_timer(const std::string& format);
                   auto_cpu_timer(std::ostream& os, short places, const std::string& format);
          explicit auto_cpu_timer(std::ostream& os, short places = default_places);
                   auto_cpu_timer(std::ostream& os, const std::string& format);

         ~auto_cpu_timer() noexcept;

          // compiler generated; shown for exposition only
          auto_cpu_timer(const auto_cpu_timer&) = default;
          auto_cpu_timer& operator=(const auto_cpu_timer&) = default;

          // observers
          std::ostream&      ostream() const noexcept;
          short              places() const noexcept;
          const std::string& format_string() const noexcept;

          // actions
          void               report();
        };

[Note: Constructors without a `std::ostream&` argument argument imply `std::cout`. An argument default is avoided as it would require including `<iostream>`, with its high costs, even when the standard streams are not used. --end note]

### `auto_cpu_timer` constructors

    explicit auto_cpu_timer(short places = default_places);
             auto_cpu_timer(short places, const std::string& format);
    explicit auto_cpu_timer(const std::string& format);
             auto_cpu_timer(std::ostream& os, short places, const std::string& format);
    explicit auto_cpu_timer(std::ostream& os, short places = default_places);
             auto_cpu_timer(std::ostream& os, const std::string& format);

> Effects: Constructs an object of type `auto_cpu_timer` and stores the ostream, places, and format string data needed to establish the postconditions.

> Postconditions:

> - For overloads with an `os` argument, `ostream() == os`. Otherwise `ostream() == std::cout`.
> - `places() == places`.
> - For overloads with a `format` argument, `format_string() == format`. Otherwise `format_string() == std::cout`

### `auto_cpu_timer` destructor

    ~auto_cpu_timer() noexcept;

> Effects: If `!is_stopped()`, stop(), report().

> [Note: Because the function is `noexcept`, implementation must ensure no exception escapes. --end note]

### auto_cpu_timer observers

The observers allow testing of constructor postconditions and specification of other functionality without resorting to "for exposition only" private members.

    std::ostream& ostream() const noexcept;

> Returns: The ostream stored by construction or subsequent copy assignment.

    short places() const noexcept;

> Returns: The places stored by construction or subsequent copy assignment.

    const std::string& format_string() const noexcept;

> Returns: The format string stored by construction or subsequent copy assignment.

### `auto_cpu_timer` actions

    void report();

> Effects: As if:

> >     ostream() << timer::format(elapsed(), places(), format_string());

> [Note: It may be desirable to call `stop()` before calling `report()` because doing I/O while the timer is running might produce misleading results. `resume()` may be called afterwards to continue timing. --end note]

## History

Beman Dawes and Rob Stewart developed version 2 of the library.

Beman did the initial development. Rob contributed many corrections, comments, and suggestions. In particular, he suggested the `resume()` and `format()` functions, resulting in improved ease-of-use for several use cases.

## Acknowledgements

Comments and suggestions came from Greg Rubino, Dave Abrahams, Vicente Botet, and John Maddock.

Revised: 08 October 2011

© Copyright Beman Dawes, 2006
© Copyright Beman Dawes and Robert Stewart, 2011

Distributed under the Boost Software License, Version 1.0. See www.boost.org/ LICENSE_1_0.txt