classification
Title: time.sleep(0.001) not working properly
Type: behavior Stage: resolved
Components: Windows Versions: Python 3.9
process
Status: closed Resolution: third party
Dependencies: Superseder:
Assigned To: Nosy List: eryksun, jack__d, paul.moore, steve.dower, steven.daprano, therenoisfood, tim.golden, zach.ware
Priority: normal Keywords:

Created on 2021-07-20 03:04 by therenoisfood, last changed 2021-07-28 07:19 by eryksun. This issue is now closed.

Files
File name Uploaded Description Edit
Capture.PNG therenoisfood, 2021-07-20 03:08
Messages (7)
msg397850 - (view) Author: Thereisfood (therenoisfood) Date: 2021-07-20 03:04
time.sleep(0.001) sleeps for 0.014 - 0.016 instead of 0.001.
msg397851 - (view) Author: Jack DeVries (jack__d) * Date: 2021-07-20 03:14
This is not a bug. See the docs:

The precision of the various real-time functions may be less than suggested by the units in which their value or argument is expressed. E.g. on most Unix systems, the clock “ticks” only 50 or 100 times a second.

On the other hand, the precision of time() and sleep() is better than their Unix equivalents: times are expressed as floating point numbers, time() returns the most accurate time available (using Unix gettimeofday() where available), and sleep() will accept a time with a nonzero fraction (Unix select() is used to implement this, where available).



Assuming a clock speed of 50x/second as the documentation names, the cpu only cycles every 0.02 seconds.
msg397856 - (view) Author: Steven D'Aprano (steven.daprano) * (Python committer) Date: 2021-07-20 06:52
Jack, Thereisfood is using Windows, which I understand has a clock with  millisecond accuracy. So a sleep of a millisecond should, I think, work on Windows even if it doesn't work on Linux.

Could a Windows expert clarify please?
msg397857 - (view) Author: Thereisfood (therenoisfood) Date: 2021-07-20 07:07
I think this is Windows 10 issue after build 1909. Because I tested on Windows 10 build 1909 is about 0.001 - 0.002 and tested on 20H2 is the attached results.
msg397863 - (view) Author: Eryk Sun (eryksun) * (Python triager) Date: 2021-07-20 08:56
The implementation of time.sleep() uses WaitForSingleObjectEx() on the main thread. It waits for an event object that gets signaled by Ctrl+C. On other threads it simply calls Sleep(). 

Thread wait functions such as WaitForSingleObjectEx() and Sleep() are based on the system interrupt time. By default the clock interrupt runs at 64 cycles per second, i.e. the interrupt time is about 15.6 ms. The interrupt time can be programmatically lowered to about 0.5 ms, but this should only be changed temporarily for timing critical applications. Lowering the interrupt time for general use can shorten the battery life on portable devices, since servicing the interrupt prevents the CPU from entering a low-power state. 

Even with a lowered interrupt time, in my experience thread dispatching in Windows simply is not implemented to support precise timing. If the wait time needs to be precise, I suggest using a loop based on time.perf_counter_ns(). Calculate a deadline based on the current counter value plus the desired wait time in nanoseconds, and loop until the current value equals or exceeds the deadline. Maybe it would be useful to implement something like this in time.sleep() itself, but I don't know whether the need in a few cases warrants the increased complexity and cost in general.
msg398332 - (view) Author: Steve Dower (steve.dower) * (Python committer) Date: 2021-07-28 00:17
> Maybe it would be useful to implement something like this in time.sleep() itself, but I don't know whether the need in a few cases warrants the increased complexity and cost in general.

It certainly wouldn't be worth the power and CPU usage impact that people would inevitable get tricked into causing ("why is Python always using 100% of my CPU!?").

So as long as I'm around, feel free to consider that idea rejected ;) We'll improve the resolution of sleep when the operating system does.
msg398352 - (view) Author: Eryk Sun (eryksun) * (Python triager) Date: 2021-07-28 07:19
> It certainly wouldn't be worth the power and CPU usage 
> impact that people would inevitable get tricked into 
> causing

To clarify, only short waits such as time.sleep(0.001) would busy loop. Waits longer than say 50 ms would call WaitForSingleObjectEx() or Sleep() with the wait time minus 50, and then busy loop until the deadline. So  time.sleep(1) would wait the thread for 95% of the interval.
History
Date User Action Args
2021-07-28 07:19:50eryksunsetmessages: + msg398352
2021-07-28 00:17:11steve.dowersetmessages: + msg398332
2021-07-20 08:56:58eryksunsetnosy: + eryksun
messages: + msg397863
resolution: third party

type: behavior
2021-07-20 07:07:56therenoisfoodsetmessages: + msg397857
2021-07-20 06:52:25steven.dapranosetnosy: + steven.daprano
messages: + msg397856
2021-07-20 03:29:40therenoisfoodsetstatus: open -> closed
stage: resolved
2021-07-20 03:14:35jack__dsetnosy: + jack__d
messages: + msg397851
2021-07-20 03:08:26therenoisfoodsetfiles: + Capture.PNG
2021-07-20 03:06:47therenoisfoodsetfiles: - Capture.PNG
2021-07-20 03:04:39therenoisfoodcreate