Search code examples
c++11c++-chrono

C++11 chrono high_resolution_clock steadyness in busy loop?


The following code utilizes C++11 std::chrono to accumulate delta time between two subsequent ticks, and outputs the number of ticks accumulated for each second interval and the average tick time over this 1 second interval.

However, under my Xcode environment, when I run the code (in Debug), the outputs seem to be much longer than 1 second apart (likely at least 2 seconds apart) - it seems that the time line is extended longer than the actual time line.

If uncommenting the sleep line, the code exhibits the correct behavior.

Can anyone tell me what's going wrong?

#include <iostream>
#include <chrono>
#include <thread>

std::chrono::high_resolution_clock::time_point then{};

double accumulator = 0.0;
size_t count = 0;

void Tick()
{
    if (then != std::chrono::high_resolution_clock::time_point{}) {
        auto delta = std::chrono::duration<double>(std::chrono::high_resolution_clock::now() - then).count();
        accumulator += delta;
        ++count;
        if (accumulator >= 1.0) {
            std::cout << "count: " << count << " | average delta: " << 1.0 / count << std::endl;
            accumulator = 0.0;
            count = 0;
        }
    } else {
        std::cout << "###" << std::endl;
    }
    then = std::chrono::high_resolution_clock::now();
}

int main(int argc, const char * argv[]) {
    // insert code here...
    std::cout << "Hello, World!\n";
    while (true) {
        Tick();

        // uncomment the following line exhibits the correct behavior
        //std::this_thread::sleep_for(std::chrono::milliseconds(1));
    }
    return 0;
}

Solution

  • So, after some digging, it is revealed that the delta computation logic together with how now is used are flawed. Several points:

    • now should only be recorded once entering the Tick function and used across the entire scope
    • the original code essentially only measures sub-parts of the entire time line as delta, that's why the accumulation of the partial delta's is less than the actual time elapsed, thus it appeared that the time is extended more than the actual time line!

    The working code looks like below:

    #include <iostream>
    #include <chrono>
    #include <thread>
    
    std::chrono::high_resolution_clock::time_point then{};
    
    double accumulator = 0.0;
    size_t count = 0;
    
    void Tick()
    {
        auto now = std::chrono::high_resolution_clock::now(); // the time point of this tick
    
        if (then != std::chrono::high_resolution_clock::time_point{}) {
            auto delta = std::chrono::duration<double>(now - then).count(); // should use the recorded time point 'now'
            accumulator += delta;
            ++count;
            if (accumulator >= 1.0) {
                std::cout << "count: " << count << " | average delta: " << 1.0 / count << std::endl;
                accumulator = 0.0;
                count = 0;
            }
        } else {
            std::cout << "###" << std::endl;
        }
        then = now; // should use the recorded time point as this tick time
    }
    
    int main(int argc, const char * argv[]) {
        // insert code here...
        std::cout << "Hello, World!\n";
        while (true) {
            Tick();
    
            // other processing
            //std::this_thread::sleep_for(std::chrono::milliseconds(1));
        }
        return 0;
    }