Timing
Timing statments can be inserted into the code usind the <chrono> header from the standard library.
std::chrono allows us to time code using a number of different clocks:
system_clock: This is the system wide real time clock.- Be aware that system time can be adjusted while your program is running (e.g. by admin) which would interfere with measurements.
steady_clock: A monotonically increasing clock which cannot be adjusted. Good for measuring time differences, but won’t give you the time of day when something happens.high_resolution_clock: The clock with the shortest ’tick’ time available on the system - this may be system_clock or steady_clock or neither, so the properties of this clock are system dependent. A useful choice when your primary concern is precision (e.g. timing functions which are fairly short), as long as you can be confident that your clock won’t be alterned during the run.
We can see an example of how to do timing using the following code fragment:
#include <iostream>
#include <chrono>
typedef std::chrono::steady_clock timingClock;
void my_function()
{
// your interesting code here
}
int main()
{
std::chrono::time_point<timingClock> t_start = timingClock::now();
my_function();
std::chrono::time_point<timingClock> t_end = timingClock::now();
std::chrono::nanoseconds diff = t_end - t_start;
std::chrono::microseconds duration = std::chrono::duration_cast<std::chrono::microseconds>(diff);
double seconds = static_cast<double>(duration.count()) * 1e-6;
std::cout << "Time taken = " << seconds << std::endl;
}
- We can take the time at a given point using the
now()function on a particular clock. - Take these times as close on either side of the thing that you want to measure as possible. Don’t put additional code (especially slow things like I/O) inside your timing statments unless you want them to contribute to yours times!
now()returns atime_pointtype. The difference of twotime_pointsis a duration type, which we can cast between different units such asnanosecondsandmicroseconds.- We can convert a duration type to a numerical using
count(), which by default is an integral type. This can be cast to a floating point type such asdoubleif you want to use the time with fractional artihmetic.
Some things to note about this code:
- As you can see, the types in
chronoare quite verbose due to the amount of namespaces! - I very strongly recommend using
typedeforusingstatments to reduce clutter (the code would have been even longer if we haven’t created the type aliastimingClock). - I’ve written out the types of everything explicitly here so that you can see what types each of these functions returns, but in practice (once you’re familiar with the way that
chronoworks) this can be a good place to useautoto de-clutter. - This code will work for any kind of clock, so you can change the clock you are using by simply changing the
typedefstatement at the top and leaving the rest of the code unchanged.
A more succinct version of this code might look like:
#include <iostream>
#include <chrono>
typedef std::chrono::steady_clock timingClock;
void my_function()
{
// Your interesting code here
}
int main()
{
auto t_start = timingClock::now();
my_function();
auto t_end = timingClock::now();
std::chrono::nanoseconds diff = t_end - t_start;
double seconds = static_cast<double>(diff.count()) * 1e-9;
std::cout << "Time taken = " << seconds << std::endl;
}
autosaves writing and reading complicated types for a function which is obvious.- It’s not obvious that a difference in two timepoints will default to nanoseconds, so that type should (in my opinion) be kept in the code for clarity. This is particularly true since it’s necessary to see that the calculation of
secondsis correct.- We can also use other types.
std::chrono::nanosecondsis actually an alias for the typestd::chrono::duration<int64_t, std::nano>. The first template parameter is the type that you want yourcountto go into (which can be integral or floating point types), and the second is essentially our units (std::nano,std::micro,std::millietc.). You can use your own template instantiationstd::chrono::duration<double, std::nano>if you want to skip thestatic_cast. - You can use
std::nanoin combination with integral types (int,int64_t,size_t,uintetc.) or floating point types (float,doubleetc.). You can usestd::microwith floating point types but if you want to have an integral count representing the number of microseconds then you need to a duration in nanoseconds first and then do aduration_cast. - Since there are so many options here, it’s a good idea to just tell people what type you’re using!
- We can also use other types.
- We showed above that we can convert to microseconds and so on, but we don’t have to do so, we can work directly with
nanosecondsif that’s useful. - It can also be a good idea to just wrap up some timing code in a little class so you can reuse it across projects and don’t have to keep thinking about all this stuff.
Close