Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python - Accurate time.sleep

I am working on a project which accurate timer is really crucial. I am working on python and am using timer.sleep() function.

I noticed that timer.sleep() function will add additional delay because of the scheduling problem (refer to timer.sleep docs). Due to that issue, the longer my program runs, the more inaccurate the timer is.

Is there any more accurate timer/ticker to sleep the program or solution for this problem?

Any help would be appreciated. Cheers.

like image 209
Darren Christopher Avatar asked May 24 '18 15:05

Darren Christopher


2 Answers

I had a solution similar to above, but it became processor heavy very quickly. Here is a processor-heavy idea and a workaround.

def processor_heavy_sleep(ms):  # fine for ms, starts to work the computer hard in second range.
    start = time.clock()
    end = start + ms /1000.
    while time.clock() < end:
        continue
    return start, time.clock()


def efficient_sleep(secs, expected_inaccuracy=0.5):  # for longer times
    start = time.clock()
    end = secs + start
    time.sleep(secs - expected_inaccuracy)
    while time.clock() < end:
        continue
    return start, time.clock()

output of efficient_sleep(5, 0.5) 3 times was:

  • (3.1999303695151594e-07, 5.0000003199930365)
  • (5.00005983869791, 10.00005983869791)
  • (10.000092477987678, 15.000092477987678)

This is on windows. I'm running it for 100 loops right now. Here are the results.

  • (485.003749358414, 490.003749358414)
  • (490.0037919174879, 495.0037922374809)
  • (495.00382903668014, 500.00382903668014)

The sleeps remain accurate, but the calls are always delayed a little. If you need a scheduler that accurately calls every xxx secs to the millisecond, that would be a different thing.

like image 130
e.s. Avatar answered Oct 04 '22 07:10

e.s.


the longer my program runs, the more inaccurate the timer is.

So, for example by expecting 0.5s delay, it will be time.sleep(0.5 - (start-end)). But still didn't solve the issue

You seem to be complaining about two effects, 1) the fact that timer.sleep() may take longer than you expect, and 2) the inherent creep in using a series of timer.sleep() calls.

You can't do anything about the first, short of switching to a real-time OS. The underlying OS calls are defined to sleep for at least as long as requested. They only guarantee that you won't wake early; they make no guarantee that you won't wake up late.

As for the second, you ought to figure your sleep time according to an unchanging epoch, not from your wake-up time. For example:

import time
import random

target = time.time()
def myticker():
    # Sleep for 0.5s between tasks, with no creep
    target += 0.5
    now = time.time()
    if target > now:
        time.sleep(target - now)


def main():
    previous = time.time()
    for _ in range(100):
        now = time.time()
        print(now - previous)
        previous = now
        # simulate some work
        time.sleep(random.random() / 10)  # Always < tick frequency
        # time.sleep(random.random())     # Not always < tick frequency
        myticker()

if __name__ == "__main__":
    main()
like image 32
Robᵩ Avatar answered Oct 04 '22 06:10

Robᵩ