关于时间:Python中的高精度时钟

High-precision clock in Python

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

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


标准的time.time()函数提供次秒精度,尽管该精度因平台而异。对于Linux和Mac,精度为+-1微秒或0.001毫秒。由于进程中断导致的时钟实现问题,Windows上的python使用+-16毫秒的精度。如果要测量执行时间,timeit模块可以提供更高的分辨率。

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

python 3.7为提供更高分辨率的time模块引入了新功能:

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


python努力使用平台最精确的时间函数来实现time.time()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/* 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?)修订版=81756&view=标记)


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

1
2
3
4
5
6
7
8
9
10
11
12
# 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

输出:

1
2
3
4
5
6
7
8
9
10
(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个样本平均的方法:

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

连续两次运行的输出:

1
2
0.001
0.0010009999275207519

所以我的Windows8x64上的time.time()的分辨率是1毫秒。

time.clock()上进行类似的运行时,返回的分辨率为0.4微秒:

1
2
3
4
5
6
7
8
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

返回:

1
4.3571334791658954e-07

是~EDOCX1[16]

time.clock()的一个有趣之处在于,它返回自首次调用该方法以来的时间,因此,如果需要微秒分辨率的壁时间,可以这样做:

1
2
3
4
5
6
7
8
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会每小时纠正一次)


您还可以使用time.clock(),它计算Unix上进程使用的时间和Windows上首次调用该进程以来的时间。它比time.time()更精确。

它是通常用来测量性能的函数。

只要打电话

1
2
3
4
import time
t_ = time.clock()
#Your code here
print 'Time in function', time.clock() - t_

编辑:UPS,我想念你想知道的问题,确切的时间,而不是花在…


如果python 3是一个选项,您有两个选择:

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

两者之间的区别可以用以下方式表示:

1
2
3
4
5
6
7
8
9
10
11
12
13
from time import (
    process_time,
    perf_counter,
    sleep,
)

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

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

输出:

1
2
3
4
0.03125
0.03125
2.560001310720671e-07
1.0005455362793145

python 3.7引入了6个纳秒级分辨率的新时间函数,例如,您可以使用time.time_ns(),而不是time.time()

1
2
3
4
5
import time
print(time.time())
# 1522915698.3436284
print(time.time_ns())
# 1522915698343660458

PEP 564中描述了这6个功能:

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()

These functions are similar to the version without the _ns suffix, but
return a number of nanoseconds as a Python int.


time.clock()在Windows上有13个小数点,但在Linux上只有两个。time.time()在Linux上有17个小数,在Windows上有16个小数,但实际精度不同。

我不同意在UNIX/Linux上使用time.clock()作为基准的文档。它不够精确,所以使用什么计时器取决于操作系统。

在Linux上,time.time()的时间分辨率很高:

1
2
>>> time.time(), time.time()
(1281384913.4374139, 1281384913.4374161)

但是,在Windows上,时间函数似乎使用最后调用的数字:

1
2
>>> 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()

(使用python 2.6和3.1在Windows7和Ubuntu9.10上测试)


Tiho在3月27日14日17:21发表的评论应该是自己的答案:

In order to avoid platform-specific code, use timeit.default_timer()


对于那些卡在Windows(version>=server 2012或win 8)和python 2.7上的用户,

1
2
3
4
5
6
7
8
9
10
11
12
import ctypes

class FILETIME(ctypes.Structure):
    _fields_ = [("dwLowDateTime", ctypes.c_uint),
                ("dwHighDateTime", ctypes.c_uint)]

def time():
   """Accurate version of time.time() for windows, return UTC time in term of seconds since 01/01/1601
"""

    file_time = FILETIME()
    ctypes.windll.kernel32.GetSystemTimePreciseAsFileTime(ctypes.byref(file_time))
    return (file_time.dwLowDateTime + (file_time.dwHighDateTime << 32)) / 1.0e7

GetSystemTimePreciseAsFileTime函数


我注意到Windows10专业版和教育版的time.time()的分辨率不同。

在Windows10专业版计算机上,分辨率为1 ms。在Windows10教育机器上,分辨率为16毫秒。

幸运的是,有一个工具可以在Windows中提高python的时间分辨率:网址:https://vvv.org/contribution/windows-system-timer-tool

使用这个工具,无论Windows版本如何,我都能获得1毫秒的分辨率。在执行Python代码时,需要保持它的运行。


最初的问题是专门针对Unix提出的,但有很多答案涉及到了Windows,因此在Windows上存在误导性信息。Windows上的默认计时器分辨率为15.6ms,您可以在此处验证。

使用cod3Monk3y中稍加修改的脚本,我可以显示Windows计时器默认分辨率为~15毫秒。我正在使用这里可用的工具来修改分辨率。

脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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')

enter image description here

enter image description here

这些结果是在运行python 3.7 64位的Windows10 Pro 64位上收集的。


1
2
3
4
5
6
7
8
9
10
11
12
13
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()