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


95

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

time.sleep(0.5)

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

time.sleep(0.05)

它真的会睡约50毫秒吗?

Answers:


78

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

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

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

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


8
实际上,Linux内核已经默认了较高的滴答频率,因此“最小”睡眠比10ms更接近1ms。这不能保证-其他系统活动可能使内核无法按您的意愿调度进程,即使没有CPU争用也是如此。我认为,这就是实时内核要解决的问题。但是,除非您真的需要实时行为,否则只需使用较高的滴答频率(内核HZ设置)即可在Linux上获得无保证但高分辨率的睡眠,而无需使用任何特殊方法。
格伦·梅纳德

1
是的,您是对的,我尝试使用Linux 2.6.24-24,并且能够获得接近1000 Hz的更新速率。当时,我也在Mac和Windows上运行代码,所以我可能会感到困惑。我知道Windows XP至少具有大约10ms的滴答速度。
约瑟夫·利瑟

在Windows 8上,我不到2毫秒
markmnl 2014年

2
此外,准确性不仅取决于操作系统,而且如果Windows和Linux忙于执行sleep()文档中更重要的事情,则操作系统在Windows和Linux上正在做什么?系统中的其他活动”。
markmnl 2014年

55

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


6
有趣的是,Linux如何选择始终比要求的时间更长的睡眠时间,而微软选择了相反的方法。
jleahy13年

2
@jleahy-Linux方法对我来说很有意义:睡眠实际上是一段时间内执行优先级的释放,之后您再次将自己提交给调度程序的意愿(可能会或可能不会立即安排您执行) 。
欠载

2
你是怎么得到结果的?您能提供源代码吗?该图看起来像是使用不同的计时器来测量时间和睡眠的产物(原则上,您甚至可以使用计时器之间的漂移作为随机性的来源)。
jfs 2015年

1
@JF Sebastian-我使用的功能在socsci.ru.nl/wilberth/computer/sleepAccuracy.html中。那里的第三张图显示了与您看到的效果相似的效果,但只有1‰。
Wilbert 2015年

1
@JF Sebastian我在Windows上使用time.clock()
Wilbert

26

文档中

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

更具体 WRT sleep()

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


1
谁能解释“因为任何捕获到的信号都会在执行该信号的捕获例程后终止sleep()”?它指的是什么信号?谢谢!
Diego Herranz

1
信号就像是操作系统管理的通知(en.wikipedia.org/wiki/Unix_signal),这意味着如果操作系统捕获到信号,则sleep()在处理该信号后完成。
ArianJM 2014年

24

这是我对Wilbert的回答的后续内容:对于Mac OS X Yosemite来说也是相同的,因为还没有被提及。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使用的完全不同的实现。


您可以将基准测试的源代码上传到github / bitbucket吗?
jfs 2015年

3
我已经在机器上尝试过结果类似于@Wilbert的答案
jfs 2015年

我猜想睡眠本身是准确的,但是Mac OS X调度的准确性不足以提供足够快的CPU,从而延迟了从睡眠中唤醒的时间。如果准确的唤醒时间很重要,则似乎应该将睡眠设置为实际请求的0.75倍,并检查唤醒后的时间,并反复多次睡眠直到正确的时间。
Mikko Rantalainen

16

为什么不找出来:

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的错误。


10
实证测试将为您提供一个非常狭窄的视角。有许多影响此的内核,操作系统和内核配置。较旧的Linux内核默认使用较低的滴答率,从而获得更大的粒度。在Unix实现中,休眠期间的外部信号会随时将其取消,其他实现可能会有类似的中断。
格伦·梅纳德

6
当然,经验观察是不能转移的。除了操作系统和内核之外,还有许多影响此问题的暂时性问题。如果需要严格的实时保证,则需要考虑从硬件到整个系统的整个设计。考虑到10ms是最低精度的说法,我刚刚发现了相关结果。我在Windows世界中并不在家,但是大多数Linux发行版已经运行了无滴答的内核已有一段时间了。随着多核技术的普及,很有可能在接近超时的情况下进行调度。
蚂蚁阿斯玛09年

4

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

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


3

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

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

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


3
文档中另有说明:>实际的暂停时间可能少于请求的暂停时间,因为任何捕获到的信号都会在执行该信号的捕获例程后终止sleep()。
格伦·梅纳德

公平的说,修正了这个帖子,尽管更长的sleeps()比短的sleeps()更有可能。
尼克·巴斯汀

1
两年半后...文档仍然存在。在Windows上,信号不会终止sleep()。已在Python 3.2,WinXP SP3上测试。
戴夫

是的,但是发出抢占睡眠的信号是不常用的,例如KILL,该文档还说:“而且,由于系统中安排了其他活动,因此暂停时间可能比请求的时间长任意数量。” 比较典型。
markmnl 2014年

1
信号和Windows只是愚蠢的。在Windows上,Python time.sleep()等待ConsoleEvent捕获Ctrl-C之类的东西。
schlenk 2014年

1

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

import time

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


0
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────────────────────

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.