如何在 python 中提高 sleep/pause 计时精度?
How to increase sleep/pause timing accuracy in python?
我 运行 一个实验来比较 sleep/pause 在 python 和 C++
中的计时精度
实验总结:
在 1000000 次迭代的循环中,每次迭代休眠 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开发一个项目,需要提高准确度。有什么想法吗?
P.S如果再推荐一个pythonlibrary/technique也可以,只要更准确:)
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
暂停库说
The precision should be within 0.001 of a second, however, this will depend on how >precise your system sleep is and other performance factors.
如果将 0.001 乘以 1000000,您将得到一个很大的累积误差。
几个问题:
你为什么需要睡觉?
最低要求的准确度是多少?
您调用的操作的时间一致性如何?如果这些函数调用的差异超过 0.001,则累积误差将更多地归因于您正在执行的操作,而不是归因于 pauses/sleeps.
Sleeping 一个线程本质上是不确定的——你不能谈论 'precision' 真的是一般的线程休眠——也许只在特定系统的上下文中平台 - 有太多因素可能发挥作用,例如有多少 cpu 个内核等。
为了说明这一点,思想实验:
假设您创建了 多个 线程(至少 1000 个)并在同一时间将它们安排到 运行。 'precision' 你会期待什么?
问题不仅在于python的睡眠定时器不准确,而且循环的每个部分都需要一些时间。
您的原始代码在我的系统上的 运行 时间为 ~1.9528656005859375。
如果我只运行你的这部分代码没有任何睡眠:
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)
然后 运行-time 变为 ~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.timedelta
和 sleep_until
都需要的 datetime
部分。
因此,如果您想获得微秒级精度,则需要寻找性能优于 datetime
的时间库。
我 运行 一个实验来比较 sleep/pause 在 python 和 C++
中的计时精度实验总结:
在 1000000 次迭代的循环中,每次迭代休眠 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开发一个项目,需要提高准确度。有什么想法吗?
P.S如果再推荐一个pythonlibrary/technique也可以,只要更准确:)
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
暂停库说
The precision should be within 0.001 of a second, however, this will depend on how >precise your system sleep is and other performance factors.
如果将 0.001 乘以 1000000,您将得到一个很大的累积误差。
几个问题:
你为什么需要睡觉?
最低要求的准确度是多少?
您调用的操作的时间一致性如何?如果这些函数调用的差异超过 0.001,则累积误差将更多地归因于您正在执行的操作,而不是归因于 pauses/sleeps.
Sleeping 一个线程本质上是不确定的——你不能谈论 'precision' 真的是一般的线程休眠——也许只在特定系统的上下文中平台 - 有太多因素可能发挥作用,例如有多少 cpu 个内核等。
为了说明这一点,思想实验:
假设您创建了 多个 线程(至少 1000 个)并在同一时间将它们安排到 运行。 'precision' 你会期待什么?
问题不仅在于python的睡眠定时器不准确,而且循环的每个部分都需要一些时间。
您的原始代码在我的系统上的 运行 时间为 ~1.9528656005859375。
如果我只运行你的这部分代码没有任何睡眠:
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)
然后 运行-time 变为 ~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.timedelta
和 sleep_until
都需要的 datetime
部分。
因此,如果您想获得微秒级精度,则需要寻找性能优于 datetime
的时间库。