1

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?

szefitoo
  • 99
  • 6
  • 1
    The accuracy also depends on the compiler's std library. For example, if compiled using mingw (on windows), then chrono may not be as accurate. – Alex A. Oct 05 '22 at 16:19
  • Are you perhaps running this on Windows? Might be related to [The default timer resolution on Windows is 15.6 ms](https://randomascii.wordpress.com/2013/07/08/windows-timer-resolution-megawatts-wasted/) – Quimby Oct 05 '22 at 16:25
  • 6
    This is going to be every OS dependent. You really need an RTOS if you want to control the timings exactly. – NathanOliver Oct 05 '22 at 16:26
  • @NathanOliver whats RTOS? – szefitoo Oct 05 '22 at 16:28
  • @szefitoo a Real Time Operating System, e.g. one without mandatory timers – Mgetz Oct 05 '22 at 16:28
  • 1
    @szefitoo https://en.wikipedia.org/wiki/Real-time_operating_system – NathanOliver Oct 05 '22 at 16:29
  • @NathanOliver so there is no way to achive it on windows? – szefitoo Oct 05 '22 at 16:29
  • can't my app force different timer resolution? – szefitoo Oct 05 '22 at 16:29
  • 4
    @szefitoo you can call [`timeBeginPeriod`](https://learn.microsoft.com/en-us/windows/win32/api/timeapi/nf-timeapi-timebeginperiod), but I'd recommend against that unless you have a really really really good reason – Mgetz Oct 05 '22 at 16:30
  • 1
    You can't measure time more accurately than the hardware/platform you are using to measure time. However, you can self-correct by checking if the overall time is wrong by an amount you can measure, and using that to inform your sleep times. – Kenny Ostrom Oct 05 '22 at 16:52
  • Related: [https://stackoverflow.com/questions/85122/how-to-make-thread-sleep-less-than-a-millisecond-on-windows](https://stackoverflow.com/questions/85122/how-to-make-thread-sleep-less-than-a-millisecond-on-windows) – drescherjm Oct 05 '22 at 17:00
  • Also: [https://stackoverflow.com/questions/58969311/increase-timer-resolution-high-resolution-on-windows-without-ntsettimerresolut](https://stackoverflow.com/questions/58969311/increase-timer-resolution-high-resolution-on-windows-without-ntsettimerresolut) – drescherjm Oct 05 '22 at 17:05
  • Also: [https://stackoverflow.com/questions/27115127/limits-of-windows-queue-timers/27121079#27121079](https://stackoverflow.com/questions/27115127/limits-of-windows-queue-timers/27121079#27121079) – drescherjm Oct 05 '22 at 17:06
  • If you have a multitasking OS (you do) and you sleep for less than that OS scheduler's standard timeslice (you do), then the only way to do it reliably is either OS-specific, **or** you should just busy wait without yielding until the time has elapsed. That is also dependent on priority & scheduler settings, of course. – Useless Oct 05 '22 at 17:07

1 Answers1

2

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.

  • It's also worth raising the priority of your application and its thread to _time critical_ (i.e. REALTIME_PRIORITY_CLASS , and THREAD_PRIORITY_TIME_CRITICAL) so that when the scheduler runs, it actually chooses your process to run. – Wyck Oct 05 '22 at 17:12
  • @Wyck: Yes, thats possible also, but can have strange side effect in the program behavior if more threads with different priorities are used, etc. Before I take "time critical", I would experiment with THREAD_PRIORITY_ABOVE_NORMAL and THREAD_PRIORITY_HIGHEST – TeaAge Solutions Oct 05 '22 at 17:22
  • Seems that it should work, however when i use it in visual studio 2022 I get linker error: – szefitoo Oct 06 '22 at 13:02
  • error LNK2019: unresolved external symbol __imp_timeBeginPeriod – szefitoo Oct 06 '22 at 13:02
  • @TeaAgeSolutions I included Timeapi.h but its still doesnt work – szefitoo Oct 06 '22 at 13:02
  • @szefitoo According to the documentation you must link to Winmm.lib. You must add it in the project settings -> Linker -> Input ->Additional Dependencies. Well, wondering why VS is not doing auto linking here, but manual add the dependency should help you. – TeaAge Solutions Oct 06 '22 at 13:23