Thursday, October 2, 2014

VC++: steady_clock is not steady



Update July 12, 2015: The problem described below is fixed in the VS2015 RC


C++11 introduced a number of new clock classes, among them system_clock and steady_clock. system_clock is meant to represent wall time; in other words, if a clock adjustment takes place or daylight saving time goes into or out of effect, it may jump forward or backward at a rate that does not reflect the actual passage of time. These properties make it ideal for reporting the actual time. steady_clock, on the other hand, is meant to always increase monotonically, making it ideal for measuring intervals between events. However, Microsoft screwed up hereas of VC++ 2013, at any rate, steady_clock jumps around with changes to the wall clock just like system_clock. I demonstrated this with the simple program below:

#include <Windows.h>
#include <chrono>
#include <iostream>

using namespace std::chrono;

void main()
{
    uint64_t gtcStart = ::GetTickCount64();
    auto steadyStart = steady_clock::now();
    auto systemStart = system_clock::now();
    auto monoStart = monotonic_clock::now();
    auto hrStart = high_resolution_clock::now();

    getchar();

    auto gtcElapsedSec = (::GetTickCount64() - gtcStart) / 1000;
    auto steadyElapsedSec = duration_cast<seconds>(steady_clock::now() - steadyStart).count();
    auto systemElapsedSec = duration_cast<seconds>(system_clock::now() - systemStart).count();
    auto monoElapsedSec = duration_cast<seconds>(monotonic_clock::now() - monoStart).count();
    auto hrElapsedSec = duration_cast<seconds>(high_resolution_clock::now() - hrStart).count();

    std::cout << "GetTickCount64(): " << gtcElapsedSec << " sec" << std::endl;
    std::cout << "steady_clock: " << steadyElapsedSec << " sec" << std::endl;
    std::cout << "system_clock: " << systemElapsedSec << " sec" << std::endl;
    std::cout << "monotonic_clock: " << monoElapsedSec << " sec" << std::endl;
    std::cout << "high_resolution_clock: " << hrElapsedSec << " sec" << std::endl;
}

While the code was sitting on getchar(), I snuck into Control Panel and set the clock ahead an hour, then returned to the program and entered a char. The output was:

GetTickCount64(): 13 sec
steady_clock: 3611 sec
system_clock: 3611 sec
monotonic_clock: 3611 sec
high_resolution_clock: 3611 sec

Now; if you're using boost in your project (and any C++ project of substance really should), boost::chrono::steady_clock has the same syntax and semantics, but actually works. Otherwise, unless you want to write your own clock, it looks like you're stuck with GetTickCount64() until the next release of Visual Studio.