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

Posted

技术标签:

【中文标题】python的time.sleep()有多准确?【英文标题】:How accurate is python's time.sleep()? 【发布时间】:2010-11-11 03:44:39 【问题描述】:

我可以给它浮点数,比如

time.sleep(0.5)

但它有多准确?如果我给它

time.sleep(0.05)

它真的会睡大约 50 毫秒吗?

【问题讨论】:

【参考方案1】:

来自documentation:

另一方面,精度 time()sleep() 比 他们的 Unix 等价物:时间是 表示为浮点数, time() 返回最准确的时间 可用(使用 Unix gettimeofday 如有),sleep() 将 接受一个非零分数的时间 (Unixselect用来实现 这个,如果有的话)。

还有more specifically w.r.t. sleep():

暂停给定数字的执行 秒。论据可能是 浮点数来表示 更精确的睡眠时间。实际上 暂停时间可能少于 请求,因为任何捕获的信号 将终止sleep() 以下 执行该信号的捕获 常规。此外,暂停时间可能 比要求的长 任意数量,因为 安排其他活动 系统。

【讨论】:

谁能解释“因为任何捕获的信号都会在执行该信号的捕获例程后终止 sleep()”?它指的是哪些信号?谢谢! 信号就像操作系统管理的通知(en.wikipedia.org/wiki/Unix_signal),这意味着如果操作系统捕获到一个信号,sleep() 会在处理完该信号后完成。【参考方案2】:

time.sleep 函数的准确性取决于您的底层操作系统的睡眠准确性。对于像普通 Windows 这样的非实时操作系统,您可以休眠的最小时间间隔约为 10-13 毫秒。我已经看到在那段时间的几毫秒内准确的睡眠时间超过了最低 10-13 毫秒。

更新: 就像下面引用的文档中提到的那样,通常在一个循环中进行睡眠,如果它早早唤醒你,它会确保重新进入睡眠状态。

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

编辑:更正非实时 Linux 内核的最小睡眠间隔更接近 1 毫秒然后 10 毫秒,但它以不确定的方式变化。

【讨论】:

实际上,Linux 内核在很长一段时间内都默认使用较高的滴答率,因此“最小”睡眠时间比 10 毫秒更接近 1 毫秒。不能保证——即使没有 CPU 争用,其他系统活动也会使内核无法按您的意愿安排您的进程。我认为这就是实时内核试图解决的问题。但是,除非您真的需要实时行为,否则只需使用高滴答率(内核 HZ 设置)即可让您在 Linux 中获得非保证但高分辨率的睡眠,而无需使用任何特殊功能。 是的,你是对的,我尝试使用 Linux 2.6.24-24 并且能够获得非常接近 1000 Hz 的更新率。在我这样做的时候,我也在 Mac 和 Windows 上运行代码,所以我可能会感到困惑。我知道 Windows XP 至少有大约 10 毫秒的滴答率。 在 Windows 8 上我得到了不到 2 毫秒 此外,准确性不仅取决于操作系统,还取决于操作系统在 Windows 和 Linux 上所做的事情,如果他们忙于做更重要的事情sleep() 来自文档“暂停时间可能会更长由于系统中其他活动的调度,而不是任意数量的请求”。【参考方案3】:

你不能真正保证 sleep() 的任何事情,除非你告诉它它至少会尽最大努力睡觉(信号可以在时间结束之前扼杀你的睡眠,还有很多事情可以让它运行很长时间)。

可以肯定的是,您可以在标准桌面操作系统上获得的最短时间约为 16 毫秒(计时器粒度加上上下文切换时间),但是当您使用'正在尝试睡眠 10 毫秒。

信号、持有 GIL 的其他线程、内核调度乐趣、处理器速度步进等都会对您的线程/进程实际休眠的持续时间造成严重破坏。

【讨论】:

文档中另有说明: > 实际的挂起时间可能少于请求的时间,因为任何捕获的信号都会在执行该信号的捕获例程后终止 sleep()。 啊,公平点,修复了这个帖子,虽然更长的 sleep() 比更短的更可能。 两年半之后......文档仍然存在。在 Windows 上,信号不会终止 sleep()。在 Python 3.2、WinXP SP3 上测试。 是的,但信号先发制人的睡眠是不寻常的,例如KILL,文档还说:“此外,由于系统中其他活动的调度,暂停时间可能比任意数量的请求长。”比较典型。 Singnals 和 Windows 实在是太傻了。在 Windows 上,Python time.sleep() 等待 ConsoleEvent 来捕获诸如 Ctrl-C 之类的东西。【参考方案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 上出现大约 0.1 毫秒的错误,而我的笔记本电脑上出现了 2 毫秒的错误,两台都是 linux 机器。

【讨论】:

经验测试会给你一个非常狭隘的观点。有许多内核、操作系统和内核配置会影响这一点。较旧的 Linux 内核默认使用较低的滴答率,这会产生更大的粒度。在 Unix 实现中,睡眠期间的外部信号会随时取消它,其他实现可能会有类似的中断。 当然,经验观察是不可转移的。除了操作系统和内核之外,还有很多暂时性的问题会影响这一点。如果需要硬实时保证,则需要考虑从硬件开始的整个系统设计。考虑到 10ms 是最低准确度的说法,我刚刚发现结果相关。我不熟悉 Windows 世界,但大多数 linux 发行版已经运行了一段时间的无滴答内核。随着现在多核的流行,它很可能在接近超时的时候被安排。【参考方案5】:

人们对于操作系统和内核之间的差异是完全正确的,但我在 Ubuntu 中没有看到任何粒度,而我在 MS7 中看到了 1 毫秒的粒度。建议 time.sleep 的不同实现,而不仅仅是不同的滴答率。顺便说一下,仔细检查表明 Ubuntu 中的粒度为 1μs,但这是由于我用于测量准确性的 time.time 函数。

【讨论】:

有趣的是,Linux 选择总是比要求的睡眠时间略长,而 Microsoft 选择了相反的方法。 @jleahy - linux 方法对我来说很有意义:睡眠实际上是在一段时间内释放执行优先级,之后您再次将自己提交给调度程序(可能会或可能会)不要立即安排你执行)。 你是怎么得到结果的?你能提供源代码吗?该图看起来像是使用不同计时器来测量时间和睡眠的人工制品(原则上,您甚至可以use the drift between the timers as a source of randomness)。 @J.F. Sebastian - 我使用的函数在 socsci.ru.nl/wilberth/computer/sleepAccuracy.html 中。那里的第三张图显示了与您看到的类似的效果,但只有 1‰。 @J.F. Sebastian 我在 windows 上使用 time.clock()【参考方案6】:

这是我对威尔伯特回答的后续:Mac OS X Yosemite 也是如此,因为它还没有被提及太多。

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

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

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

【讨论】:

能否将基准测试的源代码上传到 github/bitbucket? 我在我的机器上试过it。 The result is similar to @Wilbert's answer. 我猜睡眠本身是准确的,但 Mac OS X 调度不够准确,无法提供足够快的 CPU 速度,从而延迟从睡眠中唤醒。如果准确的唤醒时间很重要,似乎应该将睡眠设置为实际请求的 0.75 倍,并检查唤醒后的时间,并在正确的时间重复睡眠越来越少。【参考方案7】:

一个小修正,有几个人提到可以通过信号提前结束睡眠。在the 3.6 docs 它说,

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

【讨论】:

【参考方案8】:

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

【讨论】:

你是如何测试它的?【参考方案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 ────────────────────

【讨论】:

【参考方案10】:

如果您需要更精确或更短的睡眠时间,请考虑自己制作:

import time

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

【讨论】:

为什么不只是在 time.sleep() 后面实现呢?这对于短睡眠值效果更好。 很好的回答。谢谢!这就是我要找的:)【参考方案11】:
def test():
    then = time.time()  # get time at the moment
    x = 0
    while time.time() <= then+1:  # stop looping after 1 second
        x += 1
        time.sleep(0.001)  # sleep for 1 ms
    print(x)

在 windows 7 / Python 3.8 上为我返回了 1000,即使我将睡眠值设置为 0.0005

完美的 1 毫秒

【讨论】:

以上是关于python的time.sleep()有多准确?的主要内容,如果未能解决你的问题,请参考以下文章

Python time.sleep 与忙等待的准确性

更准确的 time.sleep 用于 Python 3 中的短(毫秒)延迟? [复制]

Python - time.sleep 的替代品

Python 的 time.sleep()

Python 的 time.sleep()

time.sleep(..)的准确性会更改并影响循环性能,具体取决于是否正在运行其他进程