Difference between steady_clock vs system_clock?
I am trying to see whether my data is 120 second old or not by looking at the timestamp of the data so I have below code:
uint64_t now = duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
bool is_old = (120 * 1000 < (now - data_holder->getTimestamp()));
In the above code data_holder->getTimestamp()
is uint64_t which returns timestamp in milliseconds.
Now when I print out now
variable value, I see this 10011360
and when I print out data_holder->getTimestamp()
value which is 1437520382241
so the difference of now and data holder timestamp should be negative right? Why it is coming as positive as shown in the below logs?
2015-07-21 16:13:02,530 WARN 0x7f35312d1700 data_check - now value: 10011360 , data holder timestamp: 1437520382241 , difference: 18446742636199180735
Does my above code looks right? And from the above data holder timestamp, it doesn't look to be 120 second old data right so I feel something is wrong in my code? Since if I convert that data holder timestamp to actual time (using epoch converter) and then compare it with logs time as shown above it is almost same.
I am using steady_clock
as shown above. Do I need to use system_clock
here? What is the difference between steady_clock
vs system_clock
in layman terms. I am running this code on Ubuntu 14.04 box.
Answering questions in reverse order:
What is the difference between
steady_clock
vssystem_clock
in layman terms.
If you're holding a system_clock
in your hand, you would call it a watch, and it would tell you what time it is.
If you're holding a steady_clock
in your hand, you would call it a stopwatch, and it would tell you how fast someone ran a lap, but it would not tell you what time it is.
If you had to, you could time someone running a lap with your watch. But if your watch (like mine) periodically talked to another machine (such as the atomic clock in Boulder CO) to correct itself to the current time, it might make minor mistakes in timing that lap. The stopwatch won't make that mistake, but it also can't tell you what the correct current time is.
Does my above code look right?
No. And even if it gave you reasonable answers, I would not say it is right. Don't feel bad, this is a beginner mistake that lots of people make with the <chrono>
library.
There is a simple rule I follow with the <chrono>
library. The rule is actually not completely correct (thus it is a guideline). But it is close enough to correct to be a guideline that is nearly always followed:
Don't use
count()
.
And a corollary:
Don't use
time_since_epoch()
.
The <chrono>
library is designed around a type-safe system meant to protect you from units conversions mistakes. If you accidentally attempt an unsafe conversion, the error is caught at compile time (as opposed to it being a run time error).
The member functions count()
and time_since_epoch()
are "escape hatches" out of this type-safe system ... to be used only in cases of emergency. Such emergencies arise when (for example) the committee neglects to give you all the tools you need to get the job done (such as I/O) for the <chrono>
types, or such as the need to interface with some other timing API via integers.
Review your code and other's for use of count()
and time_since_epoch()
and scrutinize each use of these functions: Is there any way the code could be rewritten to eliminate their use?
Reviewing the first line of your code:
uint64_t now = duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
now
is a time_point
(from steady_clock
). It units are milliseconds
, but at this time I'm not convinced that the units are important. What is important is that now
is a time_point
retrieved from steady_clock
:
auto now = steady_clock::now();
Your second line is more complicated:
bool is_old = (120 * 1000 < (now - data_holder->getTimestamp()));
Let's start with data_holder->getTimestamp()
: If you can modify getTimestamp()
, you should modify it to return a time_point
instead of a uint64_t
. To do so, you will have to know the correct units (which you do -- milliseconds), and you will have to know the correct epoch. The epoch is the time point against which your milliseconds are measured from.
In this case 1437520382241ms is about 45.6 years. Assuming this is a recent time stamp, 45.6 years ago was very close to 1970-01-01. As it turns out, every implementation of system_clock()
uses 1970-01-01 as its epoch (though each implementation counts different units from this epoch).
So either modify getTimestamp()
to return a time_point<system_clock, milliseconds>
, or wrap the return of getTimestamp()
with time_point<system_clock, milliseconds>
:
auto dh_ts = system_clock::time_point{milliseconds{data_holder->getTimestamp()}};
Now your second line is down to:
bool is_old = (120 * 1000 < (now - dh_ts));
Another good guideline:
If you see conversion factors in your
<chrono>
code, you're doing it wrong.<chrono>
lives for doing the conversions for you.
bool is_old = (minutes{2} < (now - dh_ts));
This next step is stylistic, but now your code is simple enough to get rid of your excess parentheses if that is something that appeals to you:
bool is_old = minutes{2} < now - dh_ts;
If you were able to modify the getTimestamp()
to return a type-safe value this code could also look like:
bool is_old = minutes{2} < now - data_holder->getTimestamp();
Alas, either way, this still does not compile! The error message should state something along the lines that that there is no valid operator-()
between now
and dh_ts
.
This is the type-safety system coming in to save you from run time errors!
The problem is that time_point
s from system_clock
can't be subtracted from time_point
s from steady_clock
(because the two have different epochs). So you have to switch to:
auto now = system_clock::now();
Putting it all together:
#include <chrono>
#include <cstdint>
#include <memory>
struct DataHolder
{
std::chrono::system_clock::time_point
getTimestamp()
{
using namespace std::chrono;
return system_clock::time_point{milliseconds{1437520382241}};
}
};
int
main()
{
using namespace std;
using namespace std::chrono;
auto data_holder = std::unique_ptr<DataHolder>(new DataHolder);
auto now = system_clock::now();
bool is_old = minutes{2} < now - data_holder->getTimestamp();
}
And in C++14 that last line can be made a little more concise:
bool is_old = 2min < now - data_holder->getTimestamp();
In summary:
- Refuse to use
count()
(except for I/O). - Refuse to use
time_since_epoch()
(except for I/O). - Refuse to use conversion factors (such as 1000).
- Argue with it until it compiles.
If you succeed in the above four points, you will most likely not experience any run time errors (but you will get your fair share of compile time errors).
First things first
The reason you're seeing a positive value is due to the unsigned integer wrap-around. Try this and see:
std::cout << static_cast <uint64_t> (-1) << std::endl;
Is the value returned by getTimestamp()
expected? If not, it's a little hard to see what's wrong, without seeing the implementation of getTimestamp()
. It looks like the timestamp was not measured using the same clock.
Steady vs. System Time
The steady clock is best for measuring time intervals. To quote from cppreference.com:
Class std::chrono::steady_clock represents a monotonic clock. The time points of this clock cannot decrease as physical time moves forward. This clock is not related to wall clock time, and is best suitable for measuring intervals.
As opposed to the system_clock, which is not monotonic (i.e. the time can decrease if, say, the user changes the time on the host machine.)
steady_clock uses the system startup time as its epoch, system_clock uses 1970-1-1 00:00 as its epoch, so there is no way to do any math between them, this makes no sense.
Before doing any subtraction between 2 unsigned integers, please make sure the minuend is larger than the subtrahend.