Difference between steady_clock vs system_clock?

32,422

Solution 1

Answering questions in reverse order:

What is the difference between steady_clock vs system_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_points from system_clock can't be subtracted from time_points 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).

Solution 2

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.)

Solution 3

  1. 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.

  2. Before doing any subtraction between 2 unsigned integers, please make sure the minuend is larger than the subtrahend.

Solution 4

First question,negative integer will implicit cast uint64_t type integer, become a huge positive integer.

Second question, system_clock is a system-wide realtime clock, and if you modified the system time as the system_clock's return time changed. The steady_clock is a physical time so that you can't change it.

Share:
32,422
user1950349
Author by

user1950349

Updated on July 09, 2022

Comments

  • user1950349
    user1950349 almost 2 years

    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.

  • user1950349
    user1950349 almost 9 years
    Thanks for great explanation. Let's chat here. I have few basic questions.
  • Angelus Mortis
    Angelus Mortis about 8 years
    Sir I'm little noob, so sorry in advance for stupid question but your statements Refuse to use count() (except for I/O). and Refuse to use time_since_epoch() (except for I/O) . So suppose I'm using some clock, I want to know duration from epoch, how do I do it without time_since_epoch() and same question for count() ?
  • Howard Hinnant
    Howard Hinnant about 8 years
    @AngelusMortis: That question is hard to answer without more context about what larger problem you are trying to solve. However my overall point is that time_since_epoch() and count() are equivalent to casts. They are escape hatches from the strong type system. Beginners tend to overuse these functions, making the type system impotent. Try your best to stay within the <chrono> type system, and you will be rewarded with the compiler detecting your logic errors at compile time (as opposed to your logic errors compiling and causing run time errors).