The C++ standard provides a std::
which provides the implementation’s clock with the smallest tick period. This gives you the best resolution available to the implementation.
But what if you don’t need the best resolution available to the implementation? For example, our task sequencer wants to wait one second, but accuracy to the nearest 100 nanoseconds is not required. If it’s one second or 0.999 seconds or 1.1 seconds, they’re all good enough.
In that case, I manually switched to the GetÂTickÂCount64
timer, which has only millisecond resolution, and in practice is good only to the nearest timer tick, which will be more like 10 milliseconds. We are using the value to decide how long to sleep, and sleeps are already fairly sloppy, so trying to be accurate to the nearest nanosecond is pointless.
What we need is a cheap_
that ticks steadily and doesn’t need the shortest tick period. Low cost is more important than precision.
For Windows, this means using the GetÂTickÂCount64
timer. Its millisecond resolution is plenty good enough for deciding how long to sleep, and the value is extremely cheap to obtain.
struct cheap_steady_clock { using rep = int64_t; using period = std::milli; using duration = std::chrono::duration<rep, period>; using time_point = std::chrono::time_point<cheap_steady_clock>; static constexpr bool is_steady = true; static time_point now() noexcept { return time_point{ duration{ static_cast<int64_t>(GetTickCount64()) } }; } };
I use a representation of int64_t
so that negative durations are representable.
For linux systems, you probably would use CLOCK_
, which is documented as a “faster but less precise version of CLOCK_
.” In practice, it is 1ms.
struct cheap_steady_clock { using duration = std::chrono::nanoseconds; using rep = duration::rep; using period = duration::period; using time_point = std::chrono::time_point<cheap_steady_clock>; static constexpr bool is_steady = true; static time_point now() noexcept { struct timespec tp; if (0 != clock_gettime(CLOCK_MONOTONIC_COARSE, &tp)) throw system_error(errno, "clock_gettime(CLOCK_MONOTONIC_COARSE) failed"); return time_point(std::chrono::seconds(tp.tv_sec) + std::chrono::nanoseconds(tp.tv_nsec)); } };
Note that the now()
method is noexcept
despite throwing an exception if it cannot read the time. The noexcept
is required by the standard, so if we can’t read the time, we have no choice but to terminate the program.
QueryPerformanceCounter (QPC), nor GetTickCount64, provide execution time gaurentees; Using the least greedy interface, as Raymond has done here, will give the best mileage and developer experience.
QPC on a blue processor (and probably red as well) may leverage a fast-path that uses only a handful of user-mode instructions, but it will resort to kernal/system calls in virtual spaces because the TSC register becomes a “public space”.
But again, depending upon the non-contractual behaviours of an interface generally goes against the spirit of Raymond’s blog, so maybe you should select a better venue for your question.
FWIW at least on my x86-64 Linux system `CLOCK_MONOTONIC_COARSE` resolution, as reported by clock_getres(), is 4ms.
Is it true that in MSVC all the std chrono clocks are just wrappers on QueryPerformanceCounter?
And is that any slower, in practice, than GetTickCount64? (I suppose it might be, if one has to do the int64 division, to get a result in milliseconds or microseconds, instead of ticks.)
I don’t know the current state of things, but ~7 years ago QPC could end up making a syscall on some Hyper-V configurations (in my case, Live Migration was enabled).
std::chrono::high_resolution_clock is not guarenteed to be a steady clock. It can be defined as an alias for std::chrono::steady_clock, std:;chrono::system_clock, or some other clock.
In this example you should pick std::chrono::steady_clock, not std::chrono::high_resolution_clock. and compare that to platform-specific options.