如何在Python中提高睡眠/暂停时间的准确性?

3

我进行了一项实验,比较了Python和C ++中的睡眠/暂停时间精度。

实验概要:

在100万次迭代的循环中,每次迭代中睡眠1微秒。

预期持续时间:1.000000秒(对于100%准确的程序)

在Python中:

import pause
import datetime

start = time.time()
dt = datetime.datetime.now()
for i in range(1000000):
    dt += datetime.timedelta(microseconds=1)
    pause.until(dt)
end = time.time()
print(end - start)

期望值:1.000000秒,实际值(约为):2.603796

使用C++语言:

#include <iostream>
#include <chrono>
#include <thread>

using namespace std;

using usec = std::chrono::microseconds;
using datetime = chrono::_V2::steady_clock::time_point;
using clk = chrono::_V2::steady_clock;

int main()
{
    datetime dt;
    usec timedelta = static_cast<usec>(1);

    dt = clk::now();

    const auto start = dt;

    for(int i=0; i < 1000000; ++i) {
        dt += timedelta;
        this_thread::sleep_until(dt);
    }

    const auto end = clk::now();

    chrono::duration<double> elapsed_seconds = end - start;

    cout << elapsed_seconds.count();

    return 0;
}

期望值:1.000000秒,实际值(大约):1.000040

显然C++更加准确,但是我正在使用Python开发一个项目,需要提高准确性。有什么建议吗?

注:如果有其他更为准确的Python库/技术,请建议使用。


pause.until(dt)不应该在循环范围内吗? - DirtyBit
@user5173426 我认为这个想法是将所有的唤醒时间延迟加起来。 - Galik
1
当您执行pause.until(1)时会发生什么? - DirtyBit
@Galik 啊!是这样吗? - DirtyBit
1
@user5173426 噢,pause.until(1) 给出了更好的精度 1.053545。但它仍然比 C++ 不够准确。 - Ahmed Hussein
显示剩余2条评论
4个回答

2

问题不仅在于Python的睡眠计时器不准确,而且循环的每个部分都需要一些时间。

您原始代码在我的系统上运行时间为约1.9528656005859375。

如果我只运行您的代码的这一部分而没有任何休眠:

Original Answer翻译成"最初的回答"

for i in range(100000):
   dt += datetime.timedelta(microseconds=1)

那么该循环所需的时间已经是约为0.45999741554260254。如果我只运行
for i in range(1000000):
   pause.milliseconds(0)

代码的运行时间约为0.5583224296569824。

始终使用相同的日期:

最初的回答:

dt = datetime.datetime.now()
for i in range(1000000):
    pause.until(dt)

运行时间约为1.326077938079834秒

如果你使用时间戳进行相同的操作:

dt = datetime.datetime.now()
ts = dt.timestamp()
for i in range(1000000):
    pause.until(ts)

然后运行时间变为约0.36722803115844727

如果你将时间戳增加一微秒:

dt = datetime.datetime.now()
ts = dt.timestamp()
for i in range(1000000):
    ts += 0.000001
    pause.until(ts)

然后您会得到约0.9536933898925781的运行时间。这个小于1是由于浮点不精确造成的。在循环后添加print(ts-dt.timestamp())将显示约为0.95367431640625,因此暂停持续时间本身是正确的,但ts += 0.000001正在累积误差。如果计算迭代次数并将iterationCount/1000000加到开始时间,您将获得最佳结果:
dt = datetime.datetime.now()
ts = dt.timestamp()
for i in range(1000000):
    pause.until(ts+i/1000000)

这将导致大约1.000023365020752的结果。在我的情况下,pause本身已经能够提供不到1微秒的准确度。问题实际上在于datetime.timedeltasleep_until所需的datetime部分。因此,如果您想要微秒级精度,则需要寻找比datetime更好的时间库。

我同意。你有什么更准确的库建议吗? - Ahmed Hussein
1
@AhmedHussein,问题不在于准确性,日期函数是_"准确的"_。问题在于这些日期函数的运行时间。您可以直接使用时间戳进行操作,这样只会有浮点数的不准确性。 - t.niese
直接在时间戳上操作是什么意思?你能提供一个示例代码吗? - Ahmed Hussein
2
@AhmedHussein 我回答中的最后两个代码块。 - t.niese
我们能将精度提高到纳秒级吗?这对我的应用程序非常有帮助。 - Ahmed Hussein
@AhmedHussein 这是一个完全不同的问题,因此您应该为其创建一个新问题。无论如何,需要暂停和微秒精度的设计已经值得怀疑。在那个设计中使用纳秒并没有太多意义,我认为您应该改变程序设计中的某些东西。 - t.niese

0

Pause库表示

精度应在0.001秒以内,但这将取决于您的系统休眠是否准确以及其他性能因素。

如果将0.001乘以1000000,则会产生较大的累积误差。

一些问题:

为什么需要休眠?

最低所需精度是多少?

您调用的操作时间上的一致性有多高?如果这些函数调用的变化超过0.001,那么由于执行的操作而产生的累积误差将比可以归因于暂停/睡眠的误差更大。


1
  • 我为什么需要这样做并不重要..这只是一个实验
  • 没有指定最小/最大准确度..我需要尽可能提高准确性
  • 关于操作,我在实验中没有添加任何操作。因为在理想情况下,我们会省略操作时间。
- Ahmed Hussein
我认为在Python中让线程休眠微秒级准确度是不可能的。 - bmat
1
顺便说一下,我认为 pause.until(1) 没有任何作用,因为它期望一个 Unix 时间戳 - https://en.wikipedia.org/wiki/Unix_time ,例如自1970年以来的毫秒数等。 - bmat
bmat的评论似乎是正确的。@user5173426请检查他的评论 :) - Ahmed Hussein

0

让线程休眠在本质上是不确定的 - 对于线程睡眠的“精确度”而言,你不能真正地谈论它的准确性 - 或许只有在特定的系统和平台环境下才能这么说 - 因为可能会影响到太多因素,例如有多少个 CPU 核心等等。

为了说明这一点,可以进行一个思想实验

假设你创建了很多线程(至少1000个),并安排它们在完全相同的时间运行。那么,你会期望它们的“精度”是什么呢?


我理解你的观点。但在我的实验中,使用了相同规格的机器,在Python和C++中都只用了1个线程。 - Ahmed Hussein

0
import pause
import datetime
import time

start = time.time()
dt = datetime.datetime.now()

for i in range(1000000):
    dt += datetime.timedelta(microseconds=1)
    pause.until(1) 
end = time.time()
print(end - start)

输出:

1.0014092922210693

1
哦,看起来你的答案不正确。根据@bmat的评论:
“顺便说一下,我认为pause.until(1)不会执行任何操作,因为它期望一个 Unix 时间戳- en.wikipedia.org/wiki/Unix_time 例如自1970年以来的毫秒数等”。
如果你移除这一行:dt += datetime.timedelta(microseconds=1) 它就不再使用1秒钟了。我是正确的吗?
- Ahmed Hussein

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接