为什么 time.sleep() 的准确性会受到 Chrome 的影响?

Why is time.sleep() accuracy influenced by Chrome?

我注意到一些奇怪的行为可能是也可能不是我的系统特有的。 (联想 t430 运行 windows 8)

用这个脚本:

import time

now = time.time()
while True:
    then = now
    now = time.time()
    dif = now - then
    print(dif)
    time.sleep(0.01)

我在打开浏览器的情况下得到以下输出(我认为是名义上的)。

但是,在没有打开浏览器的情况下,我观​​察到严重的每个循环延迟。

显然这是违反直觉的,因为我认为当并发进程较少时,任何人都会期望更好的性能。

对这些结果的任何见解或简单复制将不胜感激。

编辑: 有趣的是,我观察到这段代码有类似的延迟:

import time

now = time.time()

def newSleep(mark,duration):
    count = 0
    while time.time()-mark < duration:
        count+=1
    print(count)


while True:
    then = now
    now = time.time()
    dif = now - then
    print(dif)
    #time.sleep(0.01)
    newSleep(now,0.01)

虽然它确实提供了额外的见解——一些潜在循环的实例是由于处理器可用性不足(通过打印计数 0 注意到的)——我仍然注意到 15 毫秒的行为,其中打印计数将是高达 70k...和 ​​10ms 的行为,计数约为 40k。

Any insights or simple replication of these results would be appreciated.

给你:

使用您的代码和 Chrome 的最新版本,我可以用几乎相同的结果确认此行为。

我测量了平均花费的时间-

浏览器运行:0.01055538261329734

浏览器不支持运行:0.01563055389053695

我大约有 30 个打开的标签页,但它们都处于闲置状态。目前,我想不出会发生这种情况的任何原因。

期待进一步的见解。

我在 windows 和 ubuntu 服务器(virtualbox)(没有浏览器)中尝试了相同的方法,但结果是相同的,我得到的平均值是

在Ubuntu 服务器

    0.010122537612915039
    0.010426998138427734
    0.010067939758300781
    0.010767221450805664
    0.010728120803833008
    0.010106086730957031
    0.01068258285522461
    0.010105609893798828
    0.01118612289428711
    0.010136842727661133
    0.010585784912109375
    0.010425567626953125
    0.01014852523803711
    0.010422945022583008
    0.01010894775390625

并在 Windows

    0.010767221450805664
    0.010751485824584961
    0.010716915130615234
    0.010229110717773438
    0.01016545295715332
    0.010195255279541016
    0.010723352432250977
    0.010744094848632812
    0.010716438293457031
    0.010564565658569336
    0.010889291763305664
    0.010728597640991211
    0.010579824447631836
    0.010889530181884766
    0.010567903518676758
    0.010717153549194336
    0.010735273361206055

所以,在我看来,打开的浏览器和 python

的性能之间没有相关性

虽然我无法在我的机器上重现此行为,但我怀疑它可能是由动态处理器时钟(以及内存和系统总线时钟)引起的,例如当您没有打开浏览器时,处理器 运行 处于最大时钟的 1/2,但是一旦您启动浏览器,它就会达到最大时钟。您可以通过在高级电源选项中使用 "Maximum\minimum processor state\frequency" 或通过 运行 另一个产生许多进程的应用程序(这就是 Chrome 所做的)而不是浏览器来验证这一点,看看这是否改变了延迟。

我额外激发了 Windows 7 来复制你的发现,我可以确认它。

这是一个 Windows thing with the type of timer used and a default resolution of 15.6 ms (minimum 0.5 ms). Applications can alter the current resolution (WinAPI function: timeBeginPeriod) 并且 Chrome 也是如此。

This function affects a global Windows setting. Windows uses the lowest value (that is, highest resolution) requested by any process. Setting a higher resolution can improve the accuracy of time-out intervals in wait functions. However, it can also reduce overall system performance, because the thread scheduler switches tasks more often. High resolutions can also prevent the CPU power management system from entering power-saving modes. Setting a higher resolution does not improve the accuracy of the high-resolution performance counter.

Chrome Forbes is covering a bug 中 2014 年的一篇文章,无论当前负载需要多少,都会将分辨率 永久 设置为 1 毫秒 - 一个问题因为它是对能源消耗产生影响的全系统效应。来自那篇文章:

In an OS like Windows, events are often set to run at intervals. To save power, the processor sleeps when nothing needs attention, and wakes at predefined intervals. This interval is what Chrome adjusts in Windows, so reducing it to 1.000ms means that the system is waking far more often than at 15.625ms. In fact, at 1.000ms the processor is waking 1000 times per second. The default, of 15.625ms means the processor wakes just 64 times per second to check on events that need attention.

Microsoft itself says that tick rates of 1.000ms might increase power consumption by "as much as 25 per cent".

您可以使用 time.get_clock_info() 从 Python 获得默认分辨率。

namespace = time.get_clock_info('time')
namespace.adjustable
# True
namespace.implementation
# 'GetSystemTimeAsFileTime()'
namespace.monotonic
# False
namespace.resolution
# 0.015600099999999999

您可以使用 ClockRes 小程序从 cmd 获得实际分辨率。

我写了类似的代码并得到了类似的输出,

for i in range(10):
    then = time.time()
    time.sleep(0.01)
    now = time.time()
    # print('+---------------------------+')
    print(now - then)
    # print(f"rounded: {round(now - then, 2)}")
    # print('+---------------------------+')

我首先怀疑造成这种情况的原因是浮点数学的使用 check this one

但后来我得到了这个输出

产出

0.01080012321472168
0.01015472412109375
0.010109186172485352
0.022491931915283203 # THIS ONE IS REALLY WEIRD
0.01163029670715332
0.010137796401977539
0.010179996490478516
0.01008749008178711
0.010132551193237305
0.010088443756103516

即使您假设在计算差异时存在误差幅度,也不应该那么大

+---------------------------+
0.01080012321472168
rounded: 0.01
+---------------------------+
+---------------------------+
0.01015472412109375
rounded: 0.01
+---------------------------+
+---------------------------+
0.010109186172485352
rounded: 0.01
+---------------------------+
+---------------------------+
0.022491931915283203
rounded: 0.02                 
+---------------------------+
+---------------------------+
0.01163029670715332
rounded: 0.01
+---------------------------+
+---------------------------+
0.010137796401977539
rounded: 0.01
+---------------------------+
+---------------------------+
0.010179996490478516
rounded: 0.01
+---------------------------+
+---------------------------+
0.01008749008178711
rounded: 0.01
+---------------------------+
+---------------------------+
0.010132551193237305
rounded: 0.01
+---------------------------+
+---------------------------+
0.010088443756103516
rounded: 0.01
+---------------------------+

但是看了python的time.sleep方法的官方文档后,这些结果是有道理的check here,我引用文档

the suspension time may be longer than requested by an arbitrary amount because of the scheduling of other activity in the system

所以作为对这里问题的有根据的猜测,

多种因素共同作用

  1. 操作系统的线程调度
  2. 睡眠前后的执行时间
  3. 并且,浮点计算。

希望对您有所帮助。