0

I want to time-tag a stream of data I produce, for which I want to use std::chrono::steady_clock.

These time-stamps are stored with the data ( as array of uint64 values?), and I will later need to process these time-stamps again.

Now, I haven't been using the std::chrono library at all so far, so I do need a bit of help on the syntax and best practices with this library.

I can get & store values using:

uint64_t timestamp = std::chrono::steady_clock::now().time_since_epoch().count();

but how do I best:

  • On reading the data create a timepoint from the uint64 ?

  • Get the ticks-per-second (uint64) value for the steady_clock?

  • Find a "cut-off" timepoint (as uint64) that lies a certain time (in seconds) prior a given timepoint?

Code snippets for the above would be appreciated.

I want to combine the three above essentially to do the following: Having an array of (increasing) time-stamp values (as uint64), I want to truncate it such that all data 'older' than last-time-stamp minus X seconds is thrown away.

BmyGuest
  • 6,331
  • 1
  • 21
  • 35

3 Answers3

1

Let's have a look at the features you might use in the cppreference documentation for chrono.

First off, you need to decide which clock you want to use. There is the steady_clock which you suggested, the high_resolution_clock and the system_clock.

high_resolution_clock is implementation dependent, so let's put this away unless we really need it. The steady_clock is guaranteed to be monotonic, but there is no guarantee of the meaning for the value you are getting. It's ideal for sorting events or measuring their intervals, but you can't get a timepoint out of it.

On the other hand, system_clock has a meaning, it's the UNIX epoch, so you can get a time value out of it, but is not guaranteed to be monotonic.

To get the period (duration of one tick) of a steady_clock, you have the period member:

auto period = std::chrono::steady_clock::period();
std::cout << "Clock period " << period.num << " / " << period.den << " seconds" << std::endl;
std::cout << "Clock period " << static_cast<double>(period.num) / period.den << " seconds" << std::endl;

Assuming you want to filter events that happened in the last few seconds using steady_clock values, you first need to compute the number of ticks in the time period you want and subtract it from now. Something along the lines of:

std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
std::time_t t_c = std::chrono::system_clock::to_time_t(now - std::chrono::seconds(10));

And use t_c as cutoff point.

However, do not rely on std::chrono::steady_clock::now().time_since_epoch().count(); to get something meaningful - is just a number. The epoch for the steady_clock is usually the boot time. If you need a time, you should use system_clock (keeping in mind that is not monotonous).

C++20a introduces some more clocks, which are convertible to time.

Paul92
  • 8,827
  • 1
  • 23
  • 37
  • Thanks, this was very helpful and I'll take a look into it. I was aware of the differences between the clocks and want to use the steady_clock for exactly its properties. In your last example code line, can I subtract a uint64_t directly from a time point? Also, if I want to create a std::chrono::steady_clock::time_point from a given uint64 value, how do I do that (syntax) ? – BmyGuest Sep 15 '20 at 11:03
  • I should add, that my actual task very neatly relates to this [question & answer](https://stackoverflow.com/q/48165154/1302888) I plan on doing very much the same thing: use Steady_Clock as my accurate stopwatch plus a System_Clock start-point to relate to absolute time, if necessary. – BmyGuest Sep 15 '20 at 11:07
  • You are right about that subtraction - I edited my answer with a much better (and type safe) version. `std::time_t` is just an integral type. – Paul92 Sep 15 '20 at 11:56
  • Thanks. I'm still struggling with the chrono library though. I simply don't find enough complete example code to get me over the hurdle... In particular the "how do I get back to a timepoint if I only have a uint64 number (the one returned from a count().) – BmyGuest Sep 15 '20 at 11:58
  • I don't think that's a good idea, the whole library is geared towards getting timepoints from clocks. The workflow should be to use clocks with their timepoints, do the eventual computation you need on those, convert them to `std::time_t` or `uint64_t` and work with those. If you need anything else from a clock, you again get it in `uint64_t`. Make sure to know at every point what is that value representing - what is the epoch and the unit. – Paul92 Sep 15 '20 at 12:04
  • That's where I'm confused. I can use the counts for I/O, right? I store the counts with my data. If I then want to do something with my data and utilize any of those "stored" time-values (uint64) in the libary (for automated unit-coversions, duration-computations etc.), I need them to be time_points right (or wrong)? And, I guess, praticularly for the system_clock it makes sense to be able to "read" those values back into time-points. – BmyGuest Sep 15 '20 at 12:21
  • There are ways of creating a time point: https://en.cppreference.com/w/cpp/chrono/time_point/time_point, and there is some value in doing that. Keep in mind that a time point is basically a duration + an epoch. You can ensure that the epoch is consistent, and drop to using only durations. https://en.cppreference.com/w/cpp/chrono/duration/duration – Paul92 Sep 15 '20 at 12:35
1

As it took me far too long to figure it out from various sources today, I'm going to post my solution here as self-answer. ( I would appreciate comments on it, in case something is not correct or could be done better.)

Getting a clock's period in seconds and ticks-per-second value

    using namespace std::chrono;
    auto period = system_clock::period();
    double period_s = (double) period.num / period.den; 
    uint64 tps = period.den / period.num;

Getting a clock's timepoint (now) as uint64 value for time-stamping a data stream

    using namespace std::chrono;
    system_clock::time_point tp_now = system_clock::now();
    uint64 nowAsTicks = tp_now.time_since_epoch().count();

Getting a clock's timepoint given a stored uint64 value

    using namespace std::chrono;
    uint64 givenTicks = 12345;  // Whatever the value was
    system_clock::time_point tp_recreated = system_clock::time_point{} + system_clock::duration(givenTicks);
    uint64 recreatedTicks = tp_now.time_since_epoch().count();
    Assert( givenTicks == recreatedTicks );  // has to be true now

The last ( uint64 to timepoint ) was troubling me the most. The key-insights needed were:

  • (On Win10) The system_clock uses a time-resolution of 100 nanoseconds. Therefore one can not directly add std::chrono::nanoseconds to its native time points. (std::chrono:system_clock_time_point)

  • However, because the ticks are 100's of nanoseconds, one can also not use the next higher duration unit (microseconds) as it cannot be represent as an integer value.

  • One could use use an explicit cast to microseconds, but that would loose the 0.1us resolution of the the tick.

  • The proper way is to use the system_clock's own duration and directly initialize it with the stored tick value.

In my search I found the following resources most helpful:

BmyGuest
  • 6,331
  • 1
  • 21
  • 35
  • @HowardHinnant May I ask you to take a very quick look at this and either give an 'ok' or correct me where I'm wrong? That would be very much appreciated, thanks. – BmyGuest Sep 15 '20 at 17:24
  • 1
    Looks good to me. A couple of nitpicks: `num` and `den` are static constexpr members of the _type_ `period`, so you can ensure this computation is done at compile time if you want. It won't change the answer you get. The expression `system_clock::time_point{} + system_clock::duration(givenTicks)` is interesting. It is correct, but more verbose than the answer I would have given: `system_clock::time_point{system_clock::duration(givenTicks)}`. I kind of like your answer better as it is truer to the algebra of the type system. – Howard Hinnant Sep 16 '20 at 13:23
  • @HowardHinnant Thanks a lot. I guess it's "truer" to the system because I 'stepwise' arrived at it (if you see my edits). The whole journey has definitely been a learning curve since yesterday, but I think I *finally* got it. Strange though, that I could not find a single example code showing the time_point creation from a given tick_count anywhere... – BmyGuest Sep 16 '20 at 13:26
  • 1
    Be careful that you don't write out a `time_point` on one platform and read it in on another. In that case you might write out nanoseconds since epoch and on another machine silently reinterpret that number as some other units since epoch. If you do need to do this, you can cast all precisions to `nanoseconds` prior to write out, and then read it back in as nanoseconds and cast to `system_clock::duration` precision after reading in. – Howard Hinnant Sep 16 '20 at 13:26
  • 1
    C++ How do I convert a std::chrono::time_point to long and back: https://stackoverflow.com/a/31258680/576911 – Howard Hinnant Sep 16 '20 at 13:29
  • 1
    Oh! Why oh why didn't that post come up in any of my searches yesterday... Well, at least I had some learning-by-doing experience then. – BmyGuest Sep 16 '20 at 13:30
0

A nice place to look as usual is the reference manual :

https://en.cppreference.com/w/cpp/chrono

In this case you are looking for :

https://en.cppreference.com/w/cpp/chrono/clock_time_conversion

Since really you are using a clock with "epoch" 1/1/70 as origin and ms as unit. Then just use arithmetic on durations to do the cutoff things you want :

https://en.cppreference.com/w/cpp/chrono/duration

There are code examples at bottom of each linked page.

Yann TM
  • 1,942
  • 13
  • 22
  • Thanks for the suggestions. I've looked at these references, but the example I can not find is 'How, given a specific uint64 value' do I create a timepoint therefrom? (Maybe I'm just to blind to see it...) – BmyGuest Sep 15 '20 at 10:46