Search code examples
c++windowswinapitiming

Moving from C# to C++, QueryPerformanceCounter vs clock produce confusing results


In C# I am using the Stopwatch class. I can get the ticks, and milliseconds with no problems.

Now that I am testing code while learning C++ I try to get measurements but I don't know where the results are that match the C# Stopwatch solution equivalent. I tried to search but the information is too broad and I couldn't find an absolute solution.

double PCFreq = 0.0;
__int64 CounterStart = 0;
void StartCounter()
{
    LARGE_INTEGER li;
    if(!QueryPerformanceFrequency(&li))
    std::cout << "QueryPerformanceFrequency failed!\n";
    PCFreq = double(li.QuadPart)/1000.0;
    QueryPerformanceCounter(&li);
    CounterStart = li.QuadPart;
}

double GetCounter()
{
    LARGE_INTEGER li;
    QueryPerformanceCounter(&li);
    return double(li.QuadPart-CounterStart)/PCFreq;
}

As that gives me two different results, I tend to believe the clock. :)

start =  StartCounter()
//some function or for loop
end = GetCounter()
marginPc = end - start;

start = clock();
// ...same
end= clock();
marginClck = end - start;

std::cout<< "Res Pc: " << marginPc << "\r\nRes Clck: " marginClck<< std::endl;

With the clock version I tried both unsigned int and double but the results were still different.

What is the proper method equivalent to the C# Stopwatch?


Solution

  • clock() gives you the number of milliseconds since the program started. For example, the following program will print a number close to 500:

    int main()
    {
        Sleep(500);
        cout << clock() << endl;
    
        /*
        POSIX version:
        std::cout << clock() * 1000.0 / CLOCKS_PER_SEC << std::endl;
        CLOCKS_PER_SEC is 1000 in Windows
        */
    
        return 0;
    }
    

    QueryPerformanceCounter is sort of similar to GetTickCount64, it is based on the time when the computer started. When you do Stop-Watch type subtraction, the results are very close. QueryPerformanceCounter is more accurate. chrono method from @BoPersson's link is also based on QueryPerformanceCounter.

    MSDN recommends using QueryPerformanceCounter (QPC) for high resolution stamps:
    Acquiring high-resolution time stamps

    The same QPC function is used in managed code:

    For managed code, the System.Diagnostics.Stopwatch class uses QPC as its precise time basis

    This function should have reasonable accuracy:

    long long getmicroseconds()
    {
        LARGE_INTEGER fq, t;
        QueryPerformanceFrequency(&fq);
        QueryPerformanceCounter(&t);
        return 1000000 * t.QuadPart / fq.QuadPart;
    }
    

    The computer clock is usually accurate to +/-1 second per day.

    From above link:

    Duration          Uncertainty
    1 microsecond     ± 10 picoseconds (10-12)
    1 millisecond     ± 10 nanoseconds (10-9)
    1 second          ± 10 microseconds
    1 hour            ± 60 microseconds
    1 day             ± 0.86 seconds
    1 week            ± 6.08 seconds
    

    To simplify your other function, you can avoid double results. QuadPart is long long, so use that throughout the functions:

    long long PCFreq = 0;
    long long CounterStart = 0;
    void StartCounter()
    {
        LARGE_INTEGER li;
        QueryPerformanceFrequency(&li);
        PCFreq = li.QuadPart;
        QueryPerformanceCounter(&li);
        CounterStart = li.QuadPart;
    }
    
    long long GetCounter()
    {
        if (PCFreq < 1) return 0;
        LARGE_INTEGER li;
        QueryPerformanceCounter(&li);
    
        //for milliseconds: 1,000
        return  1000 * (li.QuadPart - CounterStart) / PCFreq;
    
        //for microseconds: 1,000,000
        //return  1000000 * (li.QuadPart - CounterStart) / PCFreq;
    }