c++sleepthread-sleep

high precision Sleep for values around 5 ms


I am looking for solution which would give me most accurate Sleep of a function for small values. I tried this code below to test it:

#include <thread>
#include <chrono>
#include <functional>
#include <windows.h>

using ms = std::chrono::milliseconds;
using us = std::chrono::microseconds;
using ns = std::chrono::nanoseconds;
const int total_test = 100;
const int sleep_time = 1;
template <typename F,typename T>
long long sleep_soluion_tester(T desired_sleep_time, F func)
{
    long long avg = 0.0;
    for (int i = 0; i < total_test; i++)
    {
        auto before = std::chrono::steady_clock::now();
        func(desired_sleep_time);
        auto after = std::chrono::steady_clock::now();
        avg += std::chrono::duration_cast<T>(after - before).count();
    }
    return avg / total_test;
};
template <typename F>
ms sleep_soluion_tester(int desired_sleep_time, F func)
{
    ms avg = ms(0);
    for (int i = 0; i < total_test; i++)
    {
        auto before = std::chrono::steady_clock::now();
        func(desired_sleep_time);
        auto after = std::chrono::steady_clock::now();
        avg += std::chrono::duration_cast<ms>(after - before);
    }
    return avg / total_test;
};
int main()
{
    auto sleep = [](auto time) {
        std::this_thread::sleep_for(time);
    };
    ms sleep_test1 = static_cast<ms>(sleep_time);
    us sleep_test2 = static_cast<us>(sleep_test1);
    ns sleep_test3 = static_cast<ns>(sleep_test1);
    std::cout << " desired sleep time: " << sleep_time << "ms\n";
    std::cout << " using Sleep(desired sleep time) "<< sleep_soluion_tester(sleep_time, Sleep).count() << "ms\n";
    std::cout << " using this_thread::sleep_for(ms) " << sleep_soluion_tester(sleep_test1,sleep) << "ms\n";
    std::cout << " using this_thread::sleep_for(us) " << sleep_soluion_tester(sleep_test2,sleep) << "ms\n";
    std::cout << " using this_thread::sleep_for(ns) " << sleep_soluion_tester(sleep_test3,sleep) << "ms\n\n";

But there seems to be no difference, they all take around 15 milliseconds to complete even when the parameter passed is 1ms. Is there a way to increase the precision of sleeping function (or some implementation that would lead to higher precision)?

I know that using thread sleep is a non-deterministic method, as the scheduler will decide when the thread will be resumed, but I wonder if there is any way to increase precision?


Solution

  • Well, sleeping exactly for a very short time cannot be guaranteed on standard desktop operating systems.

    Also, you must deal with 2 aspects:

    1. Time Measuring: If you measuring method has a low time resolution you may get completely wrong results when measuring.
    2. The time resolution of the sleeping function itself.

    Then, on Non-Real-Time-OS, like Windows, MacOS and Linux it can also happen, that your process/thread will not scheduled in time for wake up early enough. If the sleeping time is shorter, this is more likely to happen.

    So, on Windows you may use the WIN32 API and adjust the timer period and set it to the lowest possible value:

    timeBeginPeriod(1); // set period to 1 ms
    

    At the end of your task or program - according to the documentation - you must end it with

    timeEndPeriod(1);
    

    Then you can use the WIN32 Sleep function:

    Sleep(1); // (try to) sleep 1 ms
    

    According to the documentation:

    If dwMilliseconds is less than the resolution of the system clock, the thread may sleep for less than the specified length of time. If dwMilliseconds is greater than one tick but less than two, the wait can be anywhere between one and two ticks, and so on. For further information see here: https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-sleep

    On Linux you can try nanosleep, see https://man7.org/linux/man-pages/man2/nanosleep.2.html

    For time measuring you might use the QueryPerformanceCounter function on Windows and clock_gettime on Linux respectively.

    This all will not give you the guarantee to reach 1 ms resolution each time you sleep. But the timeBeginPeriod is a try, since according to the documentation the default timer resolution is 15,6 ms, which fits exactly with your measurement. Setting of a higher resolution, e.g., 1 ms may help you then. For tasks which requires "real" real-time guarantee for being functional, this will not be enough. For that Real-Time OSes are built.