Python中的高精度时钟

87

有没有一种比Python默认的一秒更精确的高精度时间测量方式?我怀疑没有跨平台的方法可以做到这一点;我对Unix上的高精度时间感兴趣,特别是在Sun SPARC机器上运行的Solaris。

timeit似乎能够进行高精度时间测量,但我想直接访问时间值而不是测量代码片段所需的时间。


2
你的意思是“经过时间”或“挂钟时间”,而不是“CPU时间”。此外,<1s不被认为是高精度。当你说“跨平台”时,你只是指“跨Linux”,还是包括Windows? - smci
2
PEP-418(介绍了time.perf_counter)和PEP-564提供了关于各种操作系统上计时性能的丰富信息,包括分辨率等表格 - djvg
16个回答

106

标准的time.time()函数提供亚秒精度,但此精度因平台而异。对于Linux和Mac平台,精度为+- 1微秒或0.001毫秒。由于时钟实现问题和处理中断,Python在Windows上使用+-16毫秒的精度。如果您要测量执行时间,则timeit模块可以提供更高的分辨率。

>>> import time
>>> time.time()        #return seconds from epoch
1261367718.971009      

Python 3.7引入了新的函数到time模块,提供了更高的分辨率:

>>> import time
>>> time.time_ns()
1530228533161016309
>>> time.time_ns() / (10 ** 9) # convert to floating-point seconds
1530228544.0792289

49
请注意,在 Windows 系统中,time.time() 函数的时间精度约为 16 毫秒。 - alexanderlukanin13
5
Windows 上不准确(无用)。 - Szabolcs Dombi
2
time_ns确实提供了纳秒级的分辨率。但在Windows上它真的很精确吗?官方文档中似乎没有相关描述。 - Jcyrss
12
@Jcyrss,“time_ns”并不具备纳秒级解决方案,它只是以纳秒的形式返回时间。这可能会或可能不会提高分辨率。有关Linux和Windows测量的时钟分辨率已记录在PEP 564中。对于Windows,测得“time_ns”的分辨率为318微秒 - wovano
9
在Python版本大于等于3.3中,答案是[time.perf_counter()] (https://docs.python.org/3.5/library/time.html#time.perf_counter)和time.process_time()。 - Berwyn
显示剩余5条评论

28

David的帖子试图展示Windows的时钟分辨率。我对他的输出感到困惑,因此我编写了一些代码,显示我的Windows 8 x64笔记本上的time.time()分辨率为1毫秒:

# measure the smallest time delta by spinning until the time changes
def measure():
    t0 = time.time()
    t1 = t0
    while t1 == t0:
        t1 = time.time()
    return (t0, t1, t1-t0)

samples = [measure() for i in range(10)]

for s in samples:
    print s

这将输出:

(1390455900.085, 1390455900.086, 0.0009999275207519531)
(1390455900.086, 1390455900.087, 0.0009999275207519531)
(1390455900.087, 1390455900.088, 0.0010001659393310547)
(1390455900.088, 1390455900.089, 0.0009999275207519531)
(1390455900.089, 1390455900.09, 0.0009999275207519531)
(1390455900.09, 1390455900.091, 0.0010001659393310547)
(1390455900.091, 1390455900.092, 0.0009999275207519531)
(1390455900.092, 1390455900.093, 0.0009999275207519531)
(1390455900.093, 1390455900.094, 0.0010001659393310547)
(1390455900.094, 1390455900.095, 0.0009999275207519531)

一种计算 delta 的 1000 次采样平均值的方法:

reduce( lambda a,b:a+b, [measure()[2] for i in range(1000)], 0.0) / 1000.0

连续运行两次会输出什么:

0.001
0.0010009999275207519

在我的Windows 8 x64上,time.time()函数的精度为1毫秒。

time.clock()函数的精度则为0.4微秒:

def measure_clock():
    t0 = time.clock()
    t1 = time.clock()
    while t1 == t0:
        t1 = time.clock()
    return (t0, t1, t1-t0)

reduce( lambda a,b:a+b, [measure_clock()[2] for i in range(1000000)] )/1000000.0

返回:

4.3571334791658954e-07

这是 ~0.4e-06

time.clock() 的有趣之处在于它返回自该方法首次被调用以来的时间��因此如果你想要微秒分辨率的墙上时间,可以像这样做:

class HighPrecisionWallTime():
    def __init__(self,):
        self._wall_time_0 = time.time()
        self._clock_0 = time.clock()

    def sample(self,):
        dc = time.clock()-self._clock_0
        return self._wall_time_0 + dc

(这可能会在一段时间后漂移,但您可以偶尔进行更正,例如dc > 3600每小时将其更正一次)


(注:该句话提供了关于如何更正漂移的建议)

这是很棒的工作,cod3monk3y...谢谢你的分享! - ojblass
6
在Windows系统上,time.clock函数可高精度地测量经过的时间; 在OS X和Linux上,它测量CPU时间。Python 3.3版本之后,建议使用perf_counter函数来测量经过的时间,使用process_time函数来测量CPU时间,time.clock已经被弃用 - George
3
这个答案是误导性的。仅仅因为你的Windows在运行此脚本时计时器分辨率设置为1ms,并不能保证另一个进程不会或不会将其设置为更高的分辨率。默认分辨率为15.6ms,任何进程都可以改变该值。我运行了你的脚本,得到了15ms的差值,然后我使用了https://github.com/tebjan/TimerTool并将其设置为1ms,再次运行它,得到了1ms的时间差。要注意不要假设Windows保持1ms的计时器分辨率,如果需要,应该在脚本开始时明确地设置它。 - Kevin S

27

Python会尽力使用最精确的时间函数来实现time.time(),以适应您的平台:

/* Implement floattime() for various platforms */

static double
floattime(void)
{
    /* There are three ways to get the time:
      (1) gettimeofday() -- resolution in microseconds
      (2) ftime() -- resolution in milliseconds
      (3) time() -- resolution in seconds
      In all cases the return value is a float in seconds.
      Since on some systems (e.g. SCO ODT 3.0) gettimeofday() may
      fail, so we fall back on ftime() or time().
      Note: clock resolution does not imply clock accuracy! */
#ifdef HAVE_GETTIMEOFDAY
    {
        struct timeval t;
#ifdef GETTIMEOFDAY_NO_TZ
        if (gettimeofday(&t) == 0)
            return (double)t.tv_sec + t.tv_usec*0.000001;
#else /* !GETTIMEOFDAY_NO_TZ */
        if (gettimeofday(&t, (struct timezone *)NULL) == 0)
            return (double)t.tv_sec + t.tv_usec*0.000001;
#endif /* !GETTIMEOFDAY_NO_TZ */
    }

#endif /* !HAVE_GETTIMEOFDAY */
    {
#if defined(HAVE_FTIME)
        struct timeb t;
        ftime(&t);
        return (double)t.time + (double)t.millitm * (double)0.001;
#else /* !HAVE_FTIME */
        time_t secs;
        time(&secs);
        return (double)secs;
#endif /* !HAVE_FTIME */
    }
}

(来自http://svn.python.org/view/python/trunk/Modules/timemodule.c?revision=81756&view=markup)

这是一个链接,指向Python官方网站上的timemodule.c文件。

23

如果Python 3是一个选项,你有两个选择:

  • time.perf_counter 总是使用您平台上最准确的时钟。它包括进程之外花费的时间。
  • time.process_time 返回CPU时间。它不包括进程之外花费的时间。

两者之间的区别可以通过以下方式显示:

from time import (
    process_time,
    perf_counter,
    sleep,
)

print(process_time())
sleep(1)
print(process_time())

print(perf_counter())
sleep(1)
print(perf_counter())

它的输出为:

0.03125
0.03125
2.560001310720671e-07
1.0005455362793145

我在Windows上尝试了这段代码,并得到了结果:0.0625、0.0625、1.0497794、2.0537843。perf_counter()的输出应该比process_time() + 1更大,是吗? - starriet
在我看来,现在应该接受这个答案。还可以查看PEP-418PEP-564中的计时结果表。后者提到,在Windows 8上使用perf_counter测量的精度为100ns - djvg

14
您也可以使用time.clock()。它在Unix上计算进程使用的时间并从第一次调用开始计时Windows操作系统的时间。比time.time()更精确。
这是通常用于测量性能的函数。
只需调用即可。
import time
t_ = time.clock()
#Your code here
print 'Time in function', time.clock() - t_

编辑:糟糕,我没有看清问题,您想要知道确切的时间,而不是花费的时间...


11

Python 3.7引入了6个新的时间函数,具有纳秒分辨率,例如,您可以使用time.time_ns()而不是time.time()来避免浮点数精度问题:

import time
print(time.time())
# 1522915698.3436284
print(time.time_ns())
# 1522915698343660458

这6个函数在PEP 564中进行了描述:

time.clock_gettime_ns(clock_id)

time.clock_settime_ns(clock_id, time:int)

time.monotonic_ns()

time.perf_counter_ns()

time.process_time_ns()

time.time_ns()

这些函数与没有 _ns 后缀的版本类似,但返回一个Python int类型的纳秒数。


很有趣的是,在树莓派3上进行基准测试。 - SDsolar
4
这些函数的准确度与原来的函数一样(或不一样)。唯一改变的是返回数据的格式:它们从浮点数变成了整数。例如,返回的1毫秒现在是1000000而不是0.001。 - Arthur Tacca

5
在Windows操作系统上,time.clock() 函数有13位小数,但在Linux上只有2位。 time.time() 在Linux上有17位小数,在Windows上有16位小数,但实际精度不同。
我不认同Python文档中关于在Unix/Linux上应该使用 time.clock() 来进行基准测试的说法。它的精确度不够,所以要使用哪个计时器取决于操作系统。
在Linux上,time.time() 的时间分辨率很高:
>>> time.time(), time.time()
(1281384913.4374139, 1281384913.4374161)

然而,在 Windows 上,time 函数似乎使用最后一次调用的数字:

>>> time.time()-int(time.time()), time.time()-int(time.time()), time.time()-time.time()
(0.9570000171661377, 0.9570000171661377, 0.0)

即使我在Windows上将调用写在不同的行上,它仍然返回相同的值,因此实际精度较低。
因此,在进行严格测量时,必须进行平台检查(import platform, platform.system()),以确定是使用time.clock()还是time.time()
(在Windows 7和Ubuntu 9.10上测试,使用python 2.6和3.1)

7
确实并不是返回最后一个值,而是在时间间隔小于时钟分辨率的情况下进行了多次调用。 - daf
你的Linux代码与Windows代码不同。Linux代码显示了两个时间值的元组,而Windows代码只显示小数部分和一个增量。如果输出类似的代码,比较起来会更容易。如果这段代码表明了什么,那就是你的Linux机器具有2.2微秒的time.time()分辨率,或者你的Linux调用需要一段时间(机器非常慢,在转换时捕获了计时器等)。我将发布一些代码,以展示如何解决你在这里提出的问题。 - cod3monk3y
5
为避免特定于平台的代码,请使用timeit.default_timer()。 - tiho

4
原问题明确要求 Unix,但多个答案涉及 Windows,因此存在误导性的Windows信息。Windows上的默认计时器分辨率为15.6毫秒,您可以在此处验证。使用稍微修改的来自cod3monk3y的脚本,我可以展示默认情况下 Windows 计时器分辨率约为 15 毫秒。我正在使用一个可用这里的工具来修改分辨率。 脚本:
import time

# measure the smallest time delta by spinning until the time changes
def measure():
    t0 = time.time()
    t1 = t0
    while t1 == t0:
        t1 = time.time()
    return t1-t0

samples = [measure() for i in range(30)]

for s in samples:
    print(f'time delta: {s:.4f} seconds') 

输入图像描述

输入图像描述

这些结果是在运行Python 3.7 64位的Windows 10 Pro 64位系统上收集的。


2
我注意到在Windows 10专业版和教育版中,time.time()的分辨率是不同的。
在Windows 10专业版上,分辨率为1毫秒。 在Windows 10教育版上,分辨率为16毫秒。
幸运的是,有一个工具可以增加Python在Windows中的时间分辨率: https://vvvv.org/contribution/windows-system-timer-tool 使用这个工具,我能够实现1毫秒的分辨率,无论Windows版本如何。您需要在执行Python代码时保持它运行。

2

这篇评论,由tiho于2014年3月27日17:21留下,值得成为一篇独立的回答:

为了避免平台特定代码,请使用timeit.default_timer()。


time.clock自3.3版本起已被弃用。 - Szabolcs Dombi

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