Let's have the following piece of code, that simply measures the duration of std::this_thread::sleep_for
called with 20ms:
#include <iostream>
#include <chrono>
#include <thread>
using namespace std;
using namespace std::chrono;
int main()
{
for (int i = 0; i < 20; i++)
{
auto start = steady_clock::now();
this_thread::sleep_for(milliseconds(20));
auto end = steady_clock::now();
duration<double, milli> elapsed = end - start;
cout << "Waited " << elapsed.count() << " ms\n";
}
}
When run as compiled with toolset v120 (VS2013's) I get results as expected, i.e.:
Waited 20.0026 ms
Waited 20.0025 ms
Waited 20.0025 ms
Waited 20.0026 ms
Waited 20.0025 ms
Waited 20.0025 ms
Waited 20.0026 ms
Waited 20.0025 ms
Waited 20.0025 ms
Waited 20.0026 ms
but when run with VS2015's toolset v140, results are somewhat surprising and don't respect the promise from both msdn and cppreference.com sleep_for
descriptions (that sleep_for
blocks the execution of the current thread for at least the specified sleep_duration
). They are as following:
Waited 19.7793 ms
Waited 19.9415 ms
Waited 19.6056 ms
Waited 19.9687 ms
Waited 19.608 ms
Waited 19.589 ms
Waited 20.5435 ms
Waited 19.5669 ms
Waited 19.6802 ms
Waited 19.5381 ms
How its possible and how can I make VS2015's sleep_for
to sleep at least as long as expected?
Regards, Dawid
EDIT:
As requested those are my settings and OS details:
OS:
Windows 7 Professional 64bit
Visual Studios: 2010 Ultimate, 2013 Community, 2015 Professional with Update 1
Compiler settings:
default settings for Win32 Console Application,
any of Debug and Release configurations,
any of x86 and x64 target platform architectures
The sleep_for()
method implementation in VS2015 already contains a loop around the Sleep()
system call, so any spurious wakeups will not affect it - see _Thrd_sleep()
in VC\crt\src\stl\cthread.c
.
The cause of your problems is most probably the fact that sleep_for()
, and sleep_until()
it calls inside, are using chrono::system_clock
to calculate the time to wait, but you are measuring the period using chrono::steady_clock
.
Both timers might have the same precision, but not necessarily the same accuracy. Then it can happen that the system_clock
is lagging a little bit, while steady_clock
is already a few μs ahead, and the wait calculated using system_clock
is actually shorter than requested.
In this exact case, steady_clock
is implemented using QueryPerformanceCounter()
(see VC\include\chrono
, search for struct steady_clock
), so it will be very accurate and precise, as that's the preferred Windows API to use for exact time measurements.
system_clock
is implemented using GetSystemTimePreciseAsFileTime()
(see VC\include\chrono
, search for struct system_clock
), which promises "the highest possible level of precision (<1us)" also. Alas, as the MSDN page says, this API is supported only from Windows 8 up and you are stuck with the older GetSystemTimeAsFileTime()
with just ms precision on your Windows 7 machine. And that is most probably the result of your measurement errors.
Depending on your exact use case, you might deal with this in different ways of course. I'd consider just ignoring the small error, as suspending the thread and waiting for the scheduler to wake it up will not be very accurate anyway.
While standard says that an implementation should use a steady_clock
for the *_for
timing functions, that is not required. And if a non-steady clock is used, then adjustments in the clock can result in the sleep time falling short. In such case the timing functions might not provide useful functionality as noted by the standard.
However, your results show consistent short falling, which I think could indicate behaviour that doesn't conform to the standard.
You can work around such behaviour by measuring the time yourself using the clock of your own choice and sleeping in a loop until the target time has passed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With