std::chrono
Today, we’ll learn how to use the
chrono
library. It’s used for timing functionality. It supports a variety of different operations and can be quite daunting in the beginning. The library contains several new types, but I’ll try to explain all of them shortly. But first a few practical tips that will make using std::chrono
easier.
using std::chrono
For working with chrono
I suggest writing using namespace std::chrono
somewhere your code ( preferably at the top ). This means you don’t have to write std::chron::
in front of everything.
1 time_point p = high_resolution_clock::now();
Is easier to read than :
1 std::chrono::time_point p = std::chrono::high_resolution_clock::now();
Usually, using namespace
statements are discouraged to avoid name collisions, but since we are writing std::chrono
we could only get name collisions with items in the std::chrono
library, which is less likely to happens.
Includes
To make sure everything compiles as it should, please add the headers and using
to the top of your file :
12345 #include <chrono>#include <ratio>// So we don't have to write std::chrono everywhere!using namespace std::chrono:
About this part
I also want to point out that this guide is rather comprehensive. This is because it’s a good idea to have deep understanding how the various components of the chrono
library works. Just writing this guide helped me to understand a lot of it. My hope is that it will help you too.
If you want more information, you can look at the en.cppreference documentation for chrono. This resource covers most, if not all of the features in C++11 with detailed information and good examples. It’s very useful for learning and as a reference.
Overview
Here is a quick overview of the chrono
library.
I will now go over each of these components, starting with the basic one, ratio
ratio
The ratio is actually not a direct part of chrono
but it’s very useful for working with chrono
and just useful in general. A ratio
is just that, a ratio or a fraction if you will. For example the ratio between a minute and a second is 60 / 1
since you need 60
seconds to make a minute. ration
is defined in it’s own header file, simply called ratio
Definition
The class is templated. The definition looks like the following :
123456 template<std::intmax_t Num,std::intmax_t Denom = 1>class ratio;
Even though this is a templated class, which can be complicated and hard to understand, the template arguments are quite straight forwards.
Template arguments :
Num
– the numerator, or simply the top part of a fractionDenom
-is the denomerator or bottom part of fraction
These two numbers forms a fractions :
Num
/Denom
Usually these numbers are dividable by 10
, so you end up with numbers like 1000
or 0.001
Both variables are of the type std::intmax_t
, which means they are the biggest possible type of signed
int
available, usually at least 64 bits. For example int64_t
.
Predefined ratios
In the chrono library ticks are stored in fractions of second. A few examples :
- A second :
ratio < 1 /1 >
- A second is
1 / 1
of a second. - Can also be written as
std::ratio< 1 >
since the denomerator defaults to1
- A second is
- A millisecond :
ratio <
1 /1000
>- A millisecond is
1 / 1000
of a second
- A millisecond is
- A minute :
ratio< 60 /1 >
- A minute is
60 / 1
seconds. - Can also be written as
ratio< 60 >
since the denomerator defaults to1
- A minute is
Resource on ratios
As a part of the ratio header, there’s a lot of predefined ratios ( milli, nano, kilo, mega, … ). Because ratio is not directly a part of the chrono
library ( it has other uses than just chrono ), there is no predefined values for minutes, hours, etc. There’s just the ones based on the standard units that’s used for distance or weight. You can refer to either of these using the std::
prefix. Since the ratio
s are not part of the chrono
library, it’s just std::
and not std::chrono::
.
For a full list of the predefined values, take a look at the documentation.
duration
duration( or std::chrono::duration
) is one of the basic classes of the chrono library. As the name suggests, it stores a time duration. Or a distance between to points in time if you like.
Member variables
It’s quite simple in its structure, it only has two member variables :
rep
- an arithmetic type ( basically any unit that can hold a number ) that stores the number of
ticks
( number of seconds, minutes, years, etc.. ). period
- a ration containing the type of unit
rep
stores. In effects this means “how many seconds/minutes/years,… is one tick.”
Constructing a duration
The creation of a duration is a bit complex as it involves two template arguments that sets representation type and a ratio plus the constructor.
teamplate arguments
The actual templated part looks like this :
1234567 code>template<class Rep,class Period = std::ratio>class duration;
Template parameters :
rep
– the type to hold the ticks- Typically
int
ordouble
ratio
– the type units used by duration- Covered above
The ratio defaults to 1 / 1
, meaning seconds
. The number of ticks
the duration
should use are sent to the constructor in the ordinary way using the parenthesis after the variable name.
To create a duration
for containing seconds, you can do :
1 duration< int64_t > durSec( 10 );
This creates a duration for holding seconds.
Other ratios
To create different durations we can use the ratio
like above. For example 10 millisecond becomes :
1 duration< int64_t, std::ratio< 1, 1000 > > durMSec( 10 );
We can simplify this by using the predefined ratio
s already in C++11
:
1 duration< int32_t, std::milli < dur( 10 );
Okay, but what about 10 minutes? This is quite similar to the above :
1 duration< int64_t, std::ratio< 60, 1 > > durMin( 10 );
We do std::ratio< 60, 1 >
because a minute is 60 / 1
seconds which or simply 60
seconds. But we can’t simply this like above because there is not predefined ratio
for minutes. The predefined ratio
s are all “clean” ratios like 1 / 100
, 1000 / 1
, 1 / 10
, 10 / 1
, etc…
Predefined durations
But there is another, even simpler way! Just like in
ratio
there are predefined values in duration
And they’re very simple to use.
So say you wanted a duration of 10 milliseconds :
1 milliseconds mSec( 10 );
Or 10 minues :
1 minutes min( 10 );
Constructor
The
duration
has three major constructors
- Default, empty constructor
- Looks something like this :
duration( )
- Initializes the
duration
- Constructor with
rep
variable - Looks something like this :
duration( rep )
where red can beint
,doube
or similar - Sets the internal tick variable to the variable passed in constructor
- This is the one we’ve used up until now
- Construct for other
duration
- Looks something like this :
duration( duration )
- Sets the internal tick variable based on the passed
duration
- The
= operator
works in the same way
From rep type
Takes a value (
rep
) as argument and sets the tick
s of the duration to it.
1 seconds durSec( 10 );
From other duration type
Takes a
duration
as argument and sets the tick
s of the duration to it. Will convert between the two types of duration based on the ration. This means that if you pass a minutes duration
to a seconds duration, you will get the number of minutes * 60 ( because there are 60 seconds in a minute.
Example
123456 minutes min( 2 );milliseconds mSec( min );// mSec will now store 120 000// 2 * 60 = 120 seconds// 120 * 1000 = 120 000 milliseconds
This creates a 10
second duration
Duration functions
Duration has a few functions, but I’ll just cover two of them now
- count
- Returns the number of ticks
- duration_cast
- Converts a duration of duration into another.
count
This function simply returns the number of ticks. So for our 10
millisecond example, this will be 10
.
Code example :
12345 minutes dur1( 1241 );milliseconds dur2( 5132 );std::cout << "dur1 duration is " << min.count() << std::endl;std::cout << "dur2 duration is " << min.count() << std::endl;
Output :
12 dur1 duration is 1241dur2 duration is 5132
duration_cast
When we’re working with two different types of duration
s things can get a bit weird. A simple example : what’s 3 minutes minus 36 seconds. The answer is quite simple for a human to figure out. But the results in your program needs to be a duration
but which unit? Since 3:24 can’t be expressed exactly in minutes, we need to change either the 3 minutes to seconds, or the 36 seconds to minutes. Here’s where duration_cast<>
comes in.
1234 minutes min( 3 );seconds sec( 36 );minutes minToSec = duration_cast< seconds <( min );
This simply casts our 3
minutes into 180
seconds. After doing that, we can simply do :
1 seconds result = minToSec - sec;
And we’ll get the result in seconds ( 144
. ) If you had converted this to minutes, it would take the whole minutes and strip the seconds. Meaning you would end up with 2
Floating points
Up until now, we’ve only used integer
values for representing ticks. This means we can only represent whole tick. So a duration
of seconds using an int
to represent ticks means it can only represent whole seconds. Consider the following code :
12345 // Duration of 16 milliseconds represented as intduration< int32_t, std::milli > msAsInt( 16 );// Try to set a duration of minutes to msAsIntduration< int32_t, std::ratio< 1, 1 > > secAsInt( msAsInt );
This will fail ( won’t compile ) because you loose precision. You could use a duration_cast
to fix this, but you’ll loose the precision and end up with 0
. duration_cast
is way of telling the compiler “yeah, I know this will loose precision, just do it!”
So instead we can create a duration of seconds represented as a double :
1234567 // Duration of 16 milliseconds represented as intduration< int32_t, std::milli > msAsInt( 16 );// Use msAsInt to set a duration of seconds represented as doubleduration< double, std::ratio< 1, 1 > > secAsDouble( msAsInt );std::cout << "Count " << secAsDouble.count() << std::endl;
Output :
Count : 0.016
This is not the same as the predefined duration
seconds
because all the predefined duration
s uses int.
Unlike the previous example, this won’t fail because you don’t loose precision. In fact, you could change the ratio
to store years :
1234 int32_t secondsInAYear = 60 * 60 * 24 * 365;std::ration year< secondsInAYear, 1 > yearsInSeconds;duration<double, yearsInSeconds > yearsAsDouble( millisecondsAsInt );
This will convert the 16 ms to years. If you did yearsAsDouble.count()
you would get roughly 5.07356672 × 10^-9
( 0.00000000507356672
) years.
Clocks
Clocks are just objects that is used for finding the current time. They are static
, which means that there will only be one of each clock type. There is also just one member function ( also static
member function, now() . The function now()
returns the current time as time_point
. I will cover time_points
later.
Clock also have a tick rate
which state how accurate they are. For instance : a clock has a tick rate
of seconds, it can’t be used to measure milliseconds. The tick rate
s are represented as ratio
s.
Clock types
There are three available clocks in the
chrono
library.
- system_clock
- Full name :
std::chrono::system_clock
- The wall clock, use this if you just want to know the current time and date
- This clock may be adjusted by either daylight saving time ( DST ) or leap seconds.
- Can be mapped to C-style points and can therefore be easily printed.
- Full name :
- steady_clock
- Full name :
std::chrono::steady_clock
- This clock is monotonic, this means will never be adjusted, so it’ll never be affected by things like DST and leap seconds
- Best suited for measuring intervals
- Full name :
- high_resolution_clock
- Full name :
std::chrono::high_resolution_clock
- Shortest tick available ( will be updated most often )
- Might be alias of std::chrono::system_clock or std::chrono::steady_clock, or a third, independent clock.
- This means it’s not guaranteed to be monotonic like steady_clock
- Use this for benchmarking
- Full name :
That’s really all you need to know about clocks, but in the next part we’ll be exploring them more.
time_point
time_point<( or
std::chrono::time_point
) is the central class of the chrono library. I’ll spare the implementation details for now and just say that it contains various information about the current point it in time.
Construction
The constructor for a time point looks something like this :
123456 template<class Clock,class Duration = typename Clock::duration>class time_point;
As you can see, its a templated class that takes both a clock, and a duration. The duration is the duration the time_point
uses. This defaults to the duration of the clock ( as you can see from the typename Clock::duration
part. )
Simplified constructor
Luckily though don’t have to specify either of the arguments. So a really simple way to construct a
time_point
is :
1 high_resolution_clock::time_point timePoint1;
You can use stady_clock
or system_clock
instead of high_resolution_clock
.
This is equal to writing :
1 time_point < high_resolution_clock > timePoint1;
A bit more complex, but it means the exact same time : a time_point
that uses a high_resolution_clock
and has the same duration as the high_resolution_clock
. I see no reason to use this method as opposed to the simpler one, but I want to explain the template arguments as well as I can.
Using custom ratio
But what if we wanted to specify our own duration? Well then we have to set the second template argument as well. Say we wanted to specify milliseconds as our duration time unit. Remember that duration has predefined constants for this.
This means we can simply do :
1 time_point < high_resolution_clock, milliseconds >
Full constructor
Now let’s do something even worse. Say we wanted a
time_point
that uses half minutes ( 30 seconds ) as the time units. I have no idea why anyone would want this, but it’s just a ( somewhat contrived ) example. As with some other examples, I do not encourage writing code like this. It’s horribly hard to read and very hard to understand unless you know the different part of the chrono
libraries. Okay, here we go :
12345678910111213 time_point<high_resolution_clock,std::chrono::duration<int64_t,std::ratio<30,1000>>>
Yup, that’s a template argument, inside a template argument, inside a template argument! Don’t write code like this! If you need a specific time ratio, at least put the ratio
and duration
in separate objects ( I’ve shown you how to use the constructors above. ) Also make sure they’re well named like ratio30sec
and duration30sec
. That should give the reader at least a small hope at knowing what’s going on.
I added this example because if you understand this constructor and how it works, you understand the major parts of std::chrono
Initialization
As stated above, the
static
member function of clocks, now()
returns a time_point
of the current point in time.
That means we can just use auto
to create a time_point
too!
1 auto time = high_resolution_clock::now()
Simple as that, we’ve created a time point. The type of this object will be exactly the same as
1 high_resolution_clock::time_point timePoint1;
Only it’s even simpler, and we’ve initialized it to the current point in time at the same time!
Difference between two time_points
And finally we can do some actual timing! Time points supports arithmetic operations. This means you can say time_point_x - time_point_y
. This will result in a duration
But what is the kind of duration. Seconds? Milliseconds? Years? Actually there is no one answer, it’s implementation defined. But the result is almost certain to be less than one second.
Since we can’t know what the resulting value is, we have two options. Say we have the following code :
12 clock::time_point t1 = std::chrono::system_clock::now();clock::time_point t2 = std::chrono::system_clock::now();
And wanted to know the result of t1 - t2
Our two options are :
- Use
auto
-
1auto result = t2 - t1 ;
- Nice and simple
- But we can’t control the type of result.
-
- Use
duration_cast
-
1milliseconds result = duration_cast< milliseconds > ( t2 - t1 ) ;
- Takes the result of
t2 - t1
and casts it into a duration of milliseconds and uses it to set result - This is a bit harder, but now we have full control over the type of duration.
- If you want higher precision, you can use microseconds or nanoseconds
- If you want higher precision, you can use microseconds or nanoseconds
- Takes the result of
-
Delta timer
So now let’s use all this knowledge and make a delta timer. A delta timer is used in games to tell the difference between two frames. The code is relatively simple and will show one ( of many ) ways to use the
chrono
library.
12345678910111213141516171819202122232425262728293031323334353637383940 #pragma once// DeltaTimer.h// Include and specify using// ( See top of post )#include <chrono>using namespace std::chrono;class Timer{public:Timer()// Init timePrev to the current point in time: timePrev( high_resolution_clock::now() ){}// Returns time since last time this function was called// in seconds with nanosecond precisiondouble GetDelta(){// 1. Get current time as a std::chrono::time_pointauto timeCurrent = high_resolution_clock::now();// 2. Get the time difference as seconds// ...represented as a doubleduration< double > delta( timeCurrent - timePrev );// 3. Reset the timePrev to the current point in timetimePrev = high_resolution_clock::now();// 4. Returns the number of ticks in deltareturn delta.count();}private:// For delta time calculation, updated every frame// We use high_resolution_clock// ...because we want the highest possible accuracytime_point< high_resolution_clock > timePrev;};
Notes
- Get the current time
- We need this to calculate the difference in the next step
- Take the dif and store it in a duration
- Stored in seconds ( since it’s the default )
- Represented as
double
- Reset
time_prev
- We need this point because we always need to know the
time_point
of the previous frame - Return the results
- Finally we return the time since last frame as fractions of a second
- Can be a very small value like
0.000000001
(1
ns )
A note about time units
This section just describes my reasoning for using seconds stored as
double
. It’s very focused around game programming and not crucial to understanding chrono
.
I choose to use seconds as double
here because double
allows higher accuracy than int
. And even though the game will almost certainly use less than 1
second on one frame, using seconds over milliseconds as time units means that the number will always be on the form 0.x
. It also ensure that the delta won’t be exactly 0
.
If the delta is 0
on one frame, no movement would happen ( I will get into this in the next SDL2
part ). If it only happens in one frame, the effect might not be that bad. But if it happens 100
frames in a row, that would be 100
frames without movements. And that would be bad!
So, in my opinion, it’s better to always have a delta of 0.x
as it ensure that there will always be at least a tiny fraction of movement. The fact that the delta is a small number can be countered by multiplying with a great number in the function that calculates movement ( we’ll see this in the next SDL2
part too! )
Conclusion
The
chrono library
is very useful and makes it easy to deal with timing. It enables high precision timing, and makes it easy to find difference between two points in time. There are also other libraries that uses chrono
like the threading
library. I might write about the threading
library later. There is also a post about timing in games coming up soon ( as mentioned in the above post. )
Feel free to comment if you have anything to say or ask questions if anything is unclear. I always appreciate getting comments.
You can also email me : olevegard@headerphile.com