Skip to content

<chrono>: Consider caching QueryPerformanceFrequency() #448

@StephanTLavavej

Description

@StephanTLavavej

steady_clock::now() says:

STL/stl/inc/chrono

Lines 612 to 614 in a83d8c0

_NODISCARD static time_point now() noexcept { // get current time
const long long _Freq = _Query_perf_frequency(); // doesn't change after system boot
const long long _Ctr = _Query_perf_counter();

STL/stl/src/xtime.cpp

Lines 92 to 96 in a83d8c0

_CRTIMP2_PURE long long __cdecl _Query_perf_frequency() { // get frequency of performance counter
LARGE_INTEGER li;
QueryPerformanceFrequency(&li); // always succeeds
return li.QuadPart;
}

As the comment indicates, QueryPerformanceFrequency documentation says: "The frequency of the performance counter is fixed at system boot and is consistent across all processors. Therefore, the frequency need only be queried upon application initialization, and the result can be cached."

This code originally used magic statics, but TFS checkin 1586419 on March 16, 2016 removed that. My checkin notes claimed (emphasis added):

Remove intentional but unnecessary use of magic statics in steady_clock::now() calling _Query_perf_frequency(). Calling QPC is very fast (when I originally profiled now(), IIRC I could call it 6-7 times before the tick changed). Calling QPF will be comparably fast, so we may as well just do that instead of paying the magic statics cost. I've left the "system boot" comment, since it's helpful to know.

I had no evidence for this performance assumption and it was incorrect. In DevCom-505019, where this issue was originally reported, Damian Zwoliński noted that while QPC is indeed efficient on most platforms (aside: IIRC, on certain VMs it is expensive), QPF is not efficient.

We're avoiding magic statics for a reason (its use of Thread Local Storage is problematic for some users), but we should investigate whether it's possible to restore caching without TLS and without breaking ABI. For example, we could have a static long long initialized to 0 (no magic) and use interlocked operations to cache QPF, since 0 is never a valid value for it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    fixedSomething works now, yay!performanceMust go faster

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions