Can Clock with direct QueryPerformanceCounter value be conforming to C++ Standard?

Viewed 287

Assuming I want to create Clock with direct QueryPerformanceCounter Windows API result. QueryPerformanceCounter Windows API returns some counter that should be divided by QueryPerformanceFrequency result, thus producing time in seconds.

Usually Clock based on QueryPerformanceCounter would immediately convert result to some units by multiplying by some period and dividing by QueryPerformanceFrequency. This is how steady_clock could be implemented on Windows.

But suppose that for performance reasons I want to avoid division until really needed. So time_point is direct QueryPerformanceCounter value, and duration is the difference of such. And I can perform arithmetic and comparison on those values most of the time, converting to some normal duration or time_point only the final result.

I'm sure it is possible. My question is: will such Clock be fully compatible with standard Clock.

2 Answers

It won't be fully compatible with the standard Clock Requirements. But it will compile and do what you want, most of the time. The part that doesn't conform is that you will have to specify something for the period that your time_point is based on. And that something won't necessarily correspond to physical time units.

This won't matter until you subtract two of these time_points, get a duration, and then compare that duration with something that does represent physical time. Then you'll get run-time garbage.

Also if you use such a time_point in sleep_until, or wait_until, then your program won't sleep or wait for the intended time.

Here is an example chrono clock based on QueryPerformanceCounter that does nail down the physical units with QueryPerformanceFrequency: https://stackoverflow.com/a/15755865/576911

Clock with redefined time_point and duration that do not correspond to the period known at compile time do no meet Clock requirements, as Howard Hinnant explained.


Using a class emulating an arithmetic type to hide additional implementation details does not work.

As Standard does not define what is this emulation, the opportunity of using emulating type is only available to the clocks provided by standard implementation. As a practical proof, std::chrono and boost::chrono do not work well with boost::rational.


However, the general task to avoid divison on each time query has at least two practical solutions.

First solution is to follow the intention to delay division until needed, but not redefining time_point and duration. Define standard-conforming clock, and, as an extension, define the interface for raw timestamp. So, clock may have raw_now() method, returning raw_time_point type, difference of such is raw_duration type. raw_time_point may be implicitly convertible to time_point and raw_duration to duration.


Second solution is to calculate standard-conforming time_point for each time query, but use libdivide library that performs fast division without division operation.

The library takes divisor and processes it so that each division using processed divisor is done using fast operations. Sure, it takes time to process divisor, but for repeated division with the same divisor it is one-time overhead. So this library looks like an ideal match for this task. (Note that the library is not useful for constants known at compile time, since in this case compiler avoids division by itself, but QueryPerformanceFrequency result is run-time constant).

Related