I have noticed that there is some strange behaviour when time.monotonic_ns
and time.sleep
interact with each other in a small time period.
To investigate this I have wrote the following "test".
import time
def main():
while True:
start = time.monotonic_ns()
time.sleep(0.01) # 10 milliseconds
end = time.monotonic_ns()
diff = end - start
print("diff: {}".format(diff))
if __name__ == "__main__":
main()
This code most of the times prints a value of about 15000000
(actually exactly 15000000 ns or 16000000 ns) or 15
milliseconds, but sometimes it is just 0
.
If I decrease the sleep-time more it has the exact same behaviour (i.e. 15 ms or 0 ns).
When I use time.time_ns
instead it will be non-zero every time (also more random and it sometimes even goes down to ~11 ms), time.perf_counter_ns
also works fine. I am aware that time.process_time
will be always 0 because it does not count sleep time. timeit.timit
also shows a minimal sleep time of ~8 ms on my system.
I am on a Windows 11 device. According to the python docs for time sleep has a resolution of 100 nanoseconds (which is clearly less than 10 milliseconds). For time.monotonic_ms()
I was not able to find a resolution.
I can understand how it could be more than the 10 milliseconds specified but I have no idea how it could be 0 nanoseconds. Python Version: 3.9.13.
I have also tested this on 3.12 with the only difference that IDLE freezes when I try to stop the execution with CTRLC.
I was made aware by a comment to this answer (found by "link hopping" based on a comment here by user "deceze ♦") of a function called time.get_clock_info
. This function provides clock-information of serval clocks (in the time-module).
This is the output for time
:
namespace(implementation='GetSystemTimeAsFileTime()', monotonic=False, adjustable=True, resolution=0.015625)
As it can be seen the resolution is about 16 ms as I have seen before.
Apparently monotonic
which is the timer for monotonic_ns
(in general all ..._ns
timers are just specialized variants of the non-ns versions as it seems) has the same resolution (with a different base):
namespace(implementation='GetTickCount64()', monotonic=True, adjustable=False, resolution=0.015625)
This still does not deliver an answer why it is sometimes zero.
perf_counter
has a higher precision:
namespace(implementation='QueryPerformanceCounter()', monotonic=True, adjustable=False, resolution=1e-07)
process_time
has the same accuracy as perf_counter
:
namespace(implementation='GetProcessTimes()', monotonic=True, adjustable=False, resolution=1e-07)
Python: 3.9.13 on Windows 11 Home (Edition of Windows seems to make a difference)
Edit: time.monotonic_ms
seems to be only able to output multiples of 16ms. So it either outputs 16 (sometimes it is off by 1 ms, this could be based of a rounding error as the actual resolution isn't 16ms but 15.625ms) or 0 in case of a shorter time period (the same goes for 20ms where it alternates between 15/16 and 31/32 ms).