-1

So I was looking for a more precise alternative to time.sleep() in Python but couldn't find any good options. Does anyone know if there is a more accurate alternative with at least millisecond precision?

Something like this:

precise_delay(3.141)

# Pauses the program for exactly 3.141 seconds

And no, I tried, and time.sleep() is not very precise.

I did some testing using time.perf_counter() and the results varied from 0.005 to 0.015 even tho I entered 0.001.

Here are the precise times: 0.013264300000628282, 0.005171099999643047 and 0.015634399999726156

Leo
  • 100
  • 10
  • 5
    You can sleep in milliseconds using `time.sleep()` what problems did you experience when you tried it? – Gerhard Oct 28 '22 at 16:42
  • 1
    What OS are you using? The [`time.sleep()` docs](https://docs.python.org/3/library/time.html#time.sleep) only give specific precisions for Windows 8.1+ and Unix. – wjandrea Oct 28 '22 at 16:50
  • 1
    `time.sleep` can certainly be *precise* to the millisecond. There are alternatives that can be more *accurate* (less affected by signals and other processor noise), if that's what you're after. – CrazyChucky Oct 28 '22 at 16:50
  • I did some testing using `time.perf_counter()` and the results varied from 5 milliseconds to 15 milliseconds – Leo Oct 28 '22 at 17:31
  • Here are the precise test results: `0.013264300000628282`, `0.005171099999643047` and `0.015634399999726156`. Remember that I entered `0.001` and I got as much as `0.015` – Leo Oct 28 '22 at 17:33
  • The code and results should be in the question. – Kelly Bundy Oct 28 '22 at 17:38
  • 2
    If you find yourself needing to sleep for an exact amount of time, you may very well have an [XY problem](https://xyproblem.info/). – kaya3 Oct 28 '22 at 18:57
  • I'm trying to make an autoclicker that waits a precise amount of time. – Leo Oct 28 '22 at 19:07
  • 1
    could it be a duplicate of https://stackoverflow.com/questions/97853/whats-the-best-way-to-synchronize-times-to-millisecond-accuracy-and-precision-b --> What's the best way to synchronize times to millisecond accuracy AND precision between machines? – pippo1980 Oct 29 '22 at 07:47

2 Answers2

3

time.sleep switches to another thread while it sleeps. That other thread will typically run for its entire time slice (10-15ms, but depends on the OS).

What you want is a spin wait that doesn't give up the thread, but uses more CPU. time.perf_counter_ns has the best precision counter:

from time import perf_counter_ns

def spinwait_us(delay):
    target = perf_counter_ns() + delay * 1000
    while perf_counter_ns() < target:
        pass

Timings:

C:\>py -m timeit -s "from test import spinwait_us" "spinwait_us(1)"
200000 loops, best of 5: 1.3 usec per loop

C:\>py -m timeit -s "from test import spinwait_us" "spinwait_us(10)"
20000 loops, best of 5: 10.3 usec per loop

C:\>py -m timeit -s "from test import spinwait_us" "spinwait_us(100)"
2000 loops, best of 5: 100 usec per loop

C:\>py -m timeit -s "from test import spinwait_us" "spinwait_us(1000)"
200 loops, best of 5: 1 msec per loop

C:\>py -m timeit -s "from test import spinwait_us" "spinwait_us(905)"
500 loops, best of 5: 905 usec per loop

Seems to run over about about 300ns on my system. So pretty accurate.

Mark Tolonen
  • 166,664
  • 26
  • 169
  • 251
  • Hmm, best-of-5 alone is not the most convincing, I think. Maybe some of the other four times were much higher... – Kelly Bundy Oct 29 '22 at 18:26
  • 1
    @kelly Yes, you can also get thread switching that adds 15ms or so if it hits at just the right time. We're not dealing with real time OSes here. But if you want to wait at least 1 ms it's much better to spinwait that to use `time.sleep`. – Mark Tolonen Oct 29 '22 at 18:28
1

Nvm I found a way to do it:

import time

def precise_delay(delay_amount):
    prv_t = time.perf_counter()

    while round((time.perf_counter() - prv_t) * 1000) / 1000 - 0.001 <= delay_amount:
        pass
Leo
  • 100
  • 10
  • 1
    A constant `while` loop like that will hog as much of your CPU as it can get. Also because you check `==` instead of `<=`, there's always a chance that it misses its target, in which case it will run indefinitely. – CrazyChucky Oct 29 '22 at 15:06
  • Good point actually, and also in this situation, it doesn't really matter that it uses a lot of CPU power – Leo Oct 29 '22 at 17:39
  • (Or to put it better, for my needs it is okay for it to use a lot of CPU power.) – Leo Oct 29 '22 at 17:54