python的time.sleep()有多准确?

问题:python的time.sleep()有多准确?

我可以给它加上浮点数,例如

time.sleep(0.5)

但是它有多精确?如果我给它

time.sleep(0.05)

它真的会睡约50毫秒吗?

I can give it floating point numbers, such as

time.sleep(0.5)

but how accurate is it? If i give it

time.sleep(0.05)

will it really sleep about 50 ms?


回答 0

time.sleep函数的准确性取决于您底层操作系统的睡眠准确性。对于非实时操作系统(如普通Windows),您可以睡眠的最小间隔约为10-13毫秒。在最小的10-13毫秒以上的时间里,我已经看到了几毫秒内的准确睡眠。

更新:就像在下面引用的文档中提到的那样,通常在一个循环中进行睡眠,这样可以确保在早起的情况下回到睡眠状态。

我还应该提到,如果您正在运行Ubuntu,则可以通过安装rt内核软件包(至少在Ubuntu 10.04 LTS中)来尝试伪实时内核(带有RT_PREEMPT补丁集)。

编辑:校正非实时Linux内核的最小睡眠间隔要比1ms再接近10ms,但是它以不确定的方式变化。

The accuracy of the time.sleep function depends on your underlying OS’s sleep accuracy. For non-realtime OS’s like a stock Windows the smallest interval you can sleep for is about 10-13ms. I have seen accurate sleeps within several milliseconds of that time when above the minimum 10-13ms.

Update: Like mentioned in the docs cited below, it’s common to do the sleep in a loop that will make sure to go back to sleep if it wakes you up early.

I should also mention that if you are running Ubuntu you can try out a pseudo real-time kernel (with the RT_PREEMPT patch set) by installing the rt kernel package (at least in Ubuntu 10.04 LTS).

EDIT: Correction non-realtime Linux kernels have minimum sleep interval much closer to 1ms then 10ms but it varies in a non-deterministic manner.


回答 1

人们对操作系统和内核之间的差异是完全正确的,但是我在Ubuntu中看不到任何粒度,在MS7中却看到1 ms的粒度。建议使用time.sleep的不同实现方式,而不仅是不同的滴答频率。仔细检查可以发现,在Ubuntu中粒度为1μs,但这是由于我用来测量精度的time.time函数所致。

People are quite right about the differences between operating systems and kernels, but I do not see any granularity in Ubuntu and I see a 1 ms granularity in MS7. Suggesting a different implementation of time.sleep, not just a different tick rate. Closer inspection suggests a 1μs granularity in Ubuntu by the way, but that is due to the time.time function that I use for measuring the accuracy.


回答 2

文档中

在另一方面,精度 time()sleep()优于他们的UNIX的等价:时间被表示为浮点数, time()返回最准确的时间(使用的Unix gettimeofday 如果有的话),和sleep()将接受具有非零分数的时间(Unix的select使用实施此操作(如果有)。

更具体 WRT sleep()

暂停执行指定的秒数。该自变量可以是浮点数,以指示更精确的睡眠时间。实际的暂停时间可能少于请求的暂停时间,因为任何捕获到的信号都会终止该sleep()信号捕获例程的后续执行。而且,由于系统中其他活动的调度,暂停时间可能比请求的时间任意数量。

From the documentation:

On the other hand, the precision of time() and sleep() is better than their Unix equivalents: times are expressed as floating point numbers, time() returns the most accurate time available (using Unix gettimeofday where available), and sleep() will accept a time with a nonzero fraction (Unix select is used to implement this, where available).

And more specifically w.r.t. sleep():

Suspend execution for the given number of seconds. The argument may be a floating point number to indicate a more precise sleep time. The actual suspension time may be less than that requested because any caught signal will terminate the sleep() following execution of that signal’s catching routine. Also, the suspension time may be longer than requested by an arbitrary amount because of the scheduling of other activity in the system.


回答 3

这是我对Wilbert的回答的后续内容:对于Mac OS X Yosemite来说也是相同的,因为还没有被提及。

看起来很多时候它的睡眠时间约为您请求时间的1.25倍,有时甚至是您请求时间的1到1.25倍之间。它几乎从来不会(约1000个样本中的两倍)睡眠时间超过您请求时间的1.25倍。

同样(未明确显示),1.25关系似乎保持良好,直到您低于约0.2 ms,此后开始变得有点模糊。此外,在请求的时间超过20 ms之后,实际时间似乎比您请求的时间稳定了大约5 ms。

同样,它似乎是sleep()OS X中与Windows或Linux内核Wilbert使用的完全不同的实现。

Here’s my follow-up to Wilbert’s answer: the same for Mac OS X Yosemite, since it’s not been mentioned much yet.

Looks like a lot of the time it sleeps about 1.25 times the time that you request and sometimes sleeps between 1 and 1.25 times the time you request. It almost never (~twice out of 1000 samples) sleeps significantly more than 1.25 times the time you request.

Also (not shown explicitly) the 1.25 relationship seems to hold pretty well until you get below about 0.2 ms, after which it starts get a little fuzzy. Additionally, the actual time seems to settle to about 5 ms longer than you request after the amount of time requested gets above 20 ms.

Again, it appears to be a completely different implementation of sleep() in OS X than in Windows or whichever Linux kernal Wilbert was using.


回答 4

为什么不找出来:

from datetime import datetime
import time

def check_sleep(amount):
    start = datetime.now()
    time.sleep(amount)
    end = datetime.now()
    delta = end-start
    return delta.seconds + delta.microseconds/1000000.

error = sum(abs(check_sleep(0.050)-0.050) for i in xrange(100))*10
print "Average error is %0.2fms" % error

作为记录,我在HTPC和笔记本电脑(均为linux机器)上均出现约0.1ms的错误。

Why don’t you find out:

from datetime import datetime
import time

def check_sleep(amount):
    start = datetime.now()
    time.sleep(amount)
    end = datetime.now()
    delta = end-start
    return delta.seconds + delta.microseconds/1000000.

error = sum(abs(check_sleep(0.050)-0.050) for i in xrange(100))*10
print "Average error is %0.2fms" % error

For the record, I get around 0.1ms error on my HTPC and 2ms on my laptop, both linux machines.


回答 5

一个小的修正,几个人提到睡眠可以通过信号提前结束。在3.6文档中

在版本3.5中进行了更改:现在,即使睡眠被信号中断,该函数也将至少睡眠几秒,除非信号处理程序引发异常(有关原理,请参阅PEP 475)。

A small correction, several people mention that sleep can be ended early by a signal. In the 3.6 docs it says,

Changed in version 3.5: The function now sleeps at least secs even if the sleep is interrupted by a signal, except if the signal handler raises an exception (see PEP 475 for the rationale).


回答 6

您不能真正保证关于sleep()的任何东西,除非它至少会尽您最大的努力使您进入睡眠状态(信号可以在时间到了之前杀死您的睡眠,还有更多事情可以使它运行)长)。

可以肯定的是,您在标准台式机操作系统上所能获得的最小值约为16毫秒(计时器粒度加上切换上下文的时间),但尝试时与提供的参数的%偏差可能会很大。睡了十毫秒。

信号,其他持有GIL的线程,内核调度的乐趣,处理器速度的提高等等,都可能在线程/进程实际睡眠的持续时间内造成严重破坏。

You can’t really guarantee anything about sleep(), except that it will at least make a best effort to sleep as long as you told it (signals can kill your sleep before the time is up, and lots more things can make it run long).

For sure the minimum you can get on a standard desktop operating system is going to be around 16ms (timer granularity plus time to context switch), but chances are that the % deviation from the provided argument is going to be significant when you’re trying to sleep for 10s of milliseconds.

Signals, other threads holding the GIL, kernel scheduling fun, processor speed stepping, etc. can all play havoc with the duration your thread/process actually sleeps.


回答 7

如果您需要更高的精度或更少的睡眠时间,请考虑自己做:

import time

def sleep(duration, get_now=time.perf_counter):
    now = get_now()
    end = now + duration
    while now < end:
        now = get_now()

if you need more precision or lower sleep times, consider making your own:

import time

def sleep(duration, get_now=time.perf_counter):
    now = get_now()
    end = now + duration
    while now < end:
        now = get_now()

回答 8

最近在Windows 10上的Python 3.7上对此进行了测试。精度约为1毫秒。

Tested this recently on Python 3.7 on Windows 10. Precision was around 1ms.


回答 9

def start(self):
    sec_arg = 10.0
    cptr = 0
    time_start = time.time()
    time_init = time.time()
    while True:
        cptr += 1
        time_start = time.time()
        time.sleep(((time_init + (sec_arg * cptr)) - time_start ))

        # AND YOUR CODE .......
        t00 = threading.Thread(name='thread_request', target=self.send_request, args=([]))
        t00.start()

不要使用变量传递sleep()的参数,必须将计算直接插入sleep()


还有我的航站楼的归还

1─────17:20:16.891──────────────────

2──────17:20:18.891────────────────────

3──────17:20:20.891──────────────────

4──────17:20:22.891──────────────────

5──────17:20:24.891──────────────────

….

689───17:43:12.891────────────────────

690───17:43:14.890────────────────────

691───17:43:16.891────────────────────

692───17:43:18.890────────────────────

693───17:43:20.891────────────────────

727───17:44:28.891────────────────────

728───17:44:30.891────────────────────

729───17:44:32.891────────────────────

730───17:44:34.890──────────────────

731───17:44:36.891────────────────────

def start(self):
    sec_arg = 10.0
    cptr = 0
    time_start = time.time()
    time_init = time.time()
    while True:
        cptr += 1
        time_start = time.time()
        time.sleep(((time_init + (sec_arg * cptr)) - time_start ))

        # AND YOUR CODE .......
        t00 = threading.Thread(name='thread_request', target=self.send_request, args=([]))
        t00.start()

Do not use a variable to pass the argument of sleep (), you must insert the calculation directly into sleep ()


And the return of my terminal

1 ───── 17:20:16.891 ───────────────────

2 ───── 17:20:18.891 ───────────────────

3 ───── 17:20:20.891 ───────────────────

4 ───── 17:20:22.891 ───────────────────

5 ───── 17:20:24.891 ───────────────────

….

689 ─── 17:43:12.891 ────────────────────

690 ─── 17:43:14.890 ────────────────────

691 ─── 17:43:16.891 ────────────────────

692 ─── 17:43:18.890 ────────────────────

693 ─── 17:43:20.891 ────────────────────

727 ─── 17:44:28.891 ────────────────────

728 ─── 17:44:30.891 ────────────────────

729 ─── 17:44:32.891 ────────────────────

730 ─── 17:44:34.890 ────────────────────

731 ─── 17:44:36.891 ────────────────────