1

Following on from this post, I've identified a non-functional implementation of Python's time.sleep() function under Windows 7 (Enterprise, 64-bit and Python 3.4.4).

Here's the reference .py script:

import threading, time

def Return():
    return

def Setup():
    for _ in range(10):
        time_before = time.time()
        Return()
        wait_delay = -time.time()

        delays = []
        InputFrequency = 60

        while (time.time() - time_before) < (1 / InputFrequency):
            time.sleep(0)

        wait_delay += time.time()

        delays.append(wait_delay)

        print("Output frequency: " + str([1/t for t in delays][0]) + " Hz")

threading.Thread(target=Setup).start()

As per this example, this script should produce output frequencies of roughly 60Hz. However, when run on my Windows 7 Enterprise machine, these are the outputs frequencies I receive for a given input frequency:

Input: 10Hz - Output: 9.15Hz


Input: 20Hz - Output: 16.03Hz


Input: 30Hz - Output 21.37Hz


Input Range: 40Hz - 64Hz - Output: 32.05Hz


Input Range: 65Hz - 10kHz+ - Output: 64.10Hz


What is going on here? Why are varying input frequencies (above 40Hz) producing the same output frequency? Why is the output frequency upper limit 64.10Hz, even when the input frequency is over 10,000Hz? I don't believe it's a time.sleep() resolution issue at ~60Hz. The same input frequency values supplied to the ideone.com script produce the expected output frequencies, so it must be related to my computer.

Community
  • 1
  • 1
jars121
  • 1,127
  • 2
  • 20
  • 35
  • Have you checked if the current version of python makes any difference? – pvg May 04 '17 at 02:45
  • I haven't, but I've just checked my Python 2.7.12 installation and it's giving the same erroneous output frequencies. – jars121 May 04 '17 at 02:46
  • Right, but your baseline is python 3 so it seems python 3.6 might be a place to start. – pvg May 04 '17 at 02:47
  • I've just installed and tried the file with Python 3.6.0 and experience the same erroneous output frequencies. – jars121 May 04 '17 at 03:01
  • Poking around I see other answers that suggest on relatively modern pythons and OS'es, sleep is about msecond accurate, give or take. Have you considered picking some sort of quantum of around that much instead of sleep(0)? – pvg May 04 '17 at 03:17
  • As a datapoint, I cannot reproduce the problem with Python 3.6.1 (Anaconda) on Windows 10 Home. – Craig May 04 '17 at 03:17
  • pvg I've tried values other than 0, but receive the same output frequencies. Craig thank you for testing, I can't imagine why I'm having this issue on my machine. – jars121 May 04 '17 at 03:21
  • I think the key thing here is to sort out whether you're missing your desired resolution because of sleep or because of inaccuracies of time.time(). there is `time.perf_counter()` which should be the most accurate available on platform. You can also record how many times your while loop is executed and what the actual time.sleep duration is. – pvg May 04 '17 at 03:37
  • I have absolutely no idea why that worked, but replacing time.time() with time.perf_counter() appears to have resolved the issue! Thank you pvg! If you'd like to submit a formal response I'll accept it as the answer. – jars121 May 04 '17 at 03:46
  • Np, glad it got sorted out. I cleaned up the answer with some details that hopefully provide some insight into why you were getting the odd results. – pvg May 04 '17 at 04:35
  • You may be experiencing the [Microsoft Minute](http://www.userfriendly.org/cartoons/archives/99mar/19990318.html). – jww Apr 30 '18 at 00:54

1 Answers1

2

Python makes very few guarantees about how these calls will behave, especially cross-platform. There are two things that can potentially go wrong here - time.time()'s resolution is worse than the resolution you're trying to achieve or sleep's resolution is.

Sleep should be roughly at least 1 to 2 ms accurate on recent platforms as reported here:

How accurate is python's time.sleep()?

This leaves time.time(). The documentation for this particular call warns the accuracy may be poor:

Note that even though the time is always returned as a floating point number, not all systems provide time with a better precision than 1 second. While this function normally returns non-decreasing values, it can return a lower value than a previous call if the system clock has been set back between the two calls.

Fortunately, a higher resolution clock API is provided in time.perf_counter() which attempts to access the highest resolution clock available on platform. From the documentation:

Return the value (in fractional seconds) of a performance counter, i.e. a clock with the highest available resolution to measure a short duration. It does include time elapsed during sleep and is system-wide. The reference point of the returned value is undefined, so that only the difference between the results of consecutive calls is valid.

In the case of Windows, this seems to be better than 60Hz which corrects your problem.

Community
  • 1
  • 1
pvg
  • 2,673
  • 4
  • 17
  • 31