Cort Ammon February 2016

Convert between c++11 clocks

If I have a time_point for an arbitrary clock (say high_resolution_clock::time_point), is there a way to convert it to a time_point for another arbitrary clock (say system_clock::time_point)?

I know there would have to be limits, if this ability existed, because not all clocks are steady, but is there any functionality to help such conversions in the spec at all?

Answers


Howard Hinnant February 2016

There's no way to do this precisely unless you know the precise duration difference between the two clock's epochs. And you don't know this for high_resolution_clock and system_clock unless is_same<high_resolution_clock, system_clock>{} is true.

That being said, you can program up an approximately correct translation and it goes much like T.C. says in his comment. Indeed, libc++ plays this trick in its implementation of condition_variable::wait_for:

https://github.com/llvm-mirror/libcxx/blob/master/include/__mutex_base#L385-L386

The calls to now of the different clocks are made as close together as possible, and one hopes that the thread isn't pre-empted between these two calls for too long. It's the best I know how to do, and the spec has wiggle room in it to allow for these types of shenanigans. E.g. something is allowed to wake up a little late, but not a little early.

In the case of libc++, the underlying OS only knows how to wait on system_clock::time_point, but the spec says you must wait on steady_clock (for good reasons). So you do what you can.

Here is a HelloWorld sketch of the idea:

#include <chrono>
#include <iostream>

std::chrono::system_clock::time_point
to_system(std::chrono::steady_clock::time_point tp)
{
    using namespace std::chrono;
    auto sys_now = system_clock::now();
    auto sdy_now = steady_clock::now();
    return time_point_cast<system_clock::duration>(tp - sdy_now + sys_now);
}

std::chrono::steady_clock::time_point
to_steady(std::chrono::system_clock::time_point tp)
{
    using namespace std::chrono;
    auto sdy_now = steady_clock::now();
    auto sys_now = system_clock::now();
    retu 


5gon12eder February 2016

I was wondering whether the accuracy of the conversion proposed by T.C. and Howard Hinnant could be improved. For reference, here is the base version that I tested.

template
<
  typename DstTimePointT,
  typename SrcTimePointT,
  typename DstClockT = typename DstTimePointT::clock,
  typename SrcClockT = typename SrcTimePointT::clock
>
DstTimePointT
clock_cast_0th(const SrcTimePointT tp)
{
  const auto src_now = SrcClockT::now();
  const auto dst_now = DstClockT::now();
  return dst_now + (tp - src_now);
}

Using the test

int
main()
{
    using namespace std::chrono;
    const auto now = system_clock::now();
    const auto steady_now = CLOCK_CAST<steady_clock::time_point>(now);
    const auto system_now = CLOCK_CAST<system_clock::time_point>(steady_now);
    const auto diff = system_now - now;
    std::cout << duration_cast<nanoseconds>(diff).count() << '\n';
}

where CLOCK_CAST would be #defined to, for now, colck_cast_0th, I collected a histogram for an idle system and one under high load. Note that this is a cold-start test. I first tried calling the function in a loop where it gives much better results. However, I think that this would give a false impression because most real-world programs probably convert a time point every now and then and will hit the cold case.

The load was generated by running the following tasks parallel to the test program. (My computer has four CPUs.)

  • A matrix multiplication benchmark (single-threaded).
  • find /usr/include -execdir grep "$(pwgen 10 1)" '{}' \; -print
  • hexdump /dev/urandom | gzip | hexdump | gzip | hexdump | gzip | hexdump | gzip | hexdump | gzip | hexdump | gzip | hexdump

Post Status

Asked in February 2016
Viewed 2,004 times
Voted 6
Answered 2 times

Search




Leave an answer