9

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.

Darren Christopher
  • 3,893
  • 4
  • 20
  • 37
  • I don't think there is, because the OS scheduler is doing its own thing. I'm monitoring this question with interest, though. – timgeb May 24 '18 at 15:01
  • On linux it's 0.001 ms inaccurate. On Windows it's around 16 ms inaccurate. It's because of OS and I'm not aware of any better solution. I guess will follow this question. – Tuğberk Kaan Duman May 24 '18 at 15:03
  • Did you tried to give your process real time priority? Accuracy is a problem of non-deterministic systems, but linux can be really close to most needs. How much delay you're willing to tolerate? ms, us, ns? Time constrained solutions are always hard to delivery and very fragile, any changes on code, kernel, or runtime in general will impact such solutions. – geckos May 24 '18 at 15:08
  • What operating system are you using? See this [tag:c++] question: [Precise thread sleep needed. Max 1ms error](https://stackoverflow.com/questions/13397571/precise-thread-sleep-needed-max-1ms-error) – Peter Wood May 24 '18 at 15:09
  • 3
    Are you calling `timer.sleep` multiple times ("*the longer my program runs, the more inaccurate the timer is*")? If so, could you correct the sleep time by keeping track of the actual time using something like `time.clock_gettime`? – cdarke May 24 '18 at 15:11
  • Also look at the last answer here: https://stackoverflow.com/questions/1211806/python-high-precision-time-sleep – anishtain4 May 24 '18 at 15:12
  • @geckos Yeah, I have tried that and still the same. As minimal as possible, I think 1ms delay is still crucial because this program will be expected to run for 1 hr. This program will read timely data for that long, so I definitely need more accurate delay. – Darren Christopher May 24 '18 at 15:14
  • @PeterWood I am using Windows. Alright, will check that out. Cheers – Darren Christopher May 24 '18 at 15:15
  • @DarrenChristopher: The trouble with Windows is that it's not a real-time OS, so as others mentioned the accuracy is already way worse than Linux. – anishtain4 May 24 '18 at 15:21
  • @cdarke yes, I am calling `timer.sleep()` at the end of my loop. Note that I have subtract the delay the whole process in my loop (i.e. by record the start using `time.time()` at the beginning of the loop and do the same before calling `time.sleep()`. So, for example by expecting 0.5s delay, it will be `time.sleep(0.5 - (start-end))`. But still didn't solve the issue. Could you elaborate code of your idea? Cheers – Darren Christopher May 24 '18 at 15:22
  • @anishtain4 so there's pretty much no solution for Windows? – Darren Christopher May 24 '18 at 15:23
  • @DarrenChristopher: my idea of using `time.clock_gettime` does not help you because you are using Windows, `timer.perf_counter` might be more appropriate. Briefly, after each sleep you calculate how much time *really* elapsed using a hi-res timer. Take the difference into account when calculating the next sleep time. – cdarke May 24 '18 at 15:30
  • @cdarke will it be similar with the approach that I use right now? By counting the start and end using `time.time()` and subtracting the delay by that? – Darren Christopher May 24 '18 at 15:32
  • @DarrenChristopher: yes, but `time.time()` only gives 1 second resolution. – cdarke May 24 '18 at 15:36
  • @cdarke I don't think so, I get this value `0.001947641372680664` by subtracting `start` and `end` time. Anyway, this approach is still not working as expected due to that scheduling issue. – Darren Christopher May 24 '18 at 15:40
  • @DarrenChristopher I wouldn't say no solution, because there are enough DAQ softwares that run on windows, but the solution would be cumbersome and only a handful of people who can help you. – anishtain4 May 24 '18 at 15:50
  • @cdarke after python 3.3 the resolution of time.time() is nano seconds. And it never was 1 second. – anishtain4 May 24 '18 at 15:51
  • how long is each sleep period? – e.s. May 24 '18 at 16:07
  • Also, consider https://docs.python.org/3/library/sched.html – Robᵩ May 24 '18 at 17:07
  • @anishtain4: OK, just going from the doc. – cdarke May 24 '18 at 21:25
  • For 1ms delay I would try a kind of busy loop. Keep in mind that your process may be scheduled by the kernel, even when you are not sleeping, making your process randomly take more time than desired.. again, non-determinism. If you need something really accurate, always then you need a RTOS – geckos May 25 '18 at 18:46

3 Answers3

4

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.

e.s.
  • 1,351
  • 8
  • 12
1

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()
Community
  • 1
  • 1
Robᵩ
  • 163,533
  • 20
  • 239
  • 308
0

Working on Linux with zero knowledge of Windows, I may be being naive here but is there some reason that writing your own sleep function, won't work for you?
Something like:

import time

def sleep_time():
    start_time = time.time()
    while (time.time() - start_time) < 0.0001:
        continue

end_time = time.time() + 60 # run for a minute
cnt = 0
while time.time() < end_time:
    cnt += 1
    print('sleeping',cnt)
    sleep_time()
    print('Awake')
print("Slept ",cnt," Times")
Rolf of Saxony
  • 21,661
  • 5
  • 39
  • 60