Python中的高精度时钟

Posted

技术标签:

【中文标题】Python中的高精度时钟【英文标题】:High-precision clock in Python 【发布时间】:2010-12-28 15:00:53 【问题描述】:

有没有办法在 Python 中以高精度测量时间——比一秒更精确?我怀疑是否有跨平台的方式来做到这一点;我对 Unix 上的高精度时间感兴趣,尤其是在 Sun SPARC 机器上运行的 Solaris。

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

【问题讨论】:

您的意思是“经过的时间”或“挂钟时间”,而不是“CPU 时间”。此外, 相关问答:How to get millisecond and microsecond-resolution timestamps in Python PEP-418(其中引入了time.perf_counter)和PEP-564 提供了有关各种操作系统的时序性能的大量信息,包括用于解析的,等等 【参考方案1】:

标准time.time() 函数提供亚秒级精度,但该精度因平台而异。对于 Linux 和 Mac,精度为 +- 1 微秒或 0.001 毫秒。由于进程中断导致的时钟实现问题,Windows 上的 Python 使用 +- 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

【讨论】:

请注意,在 Windows 上 time.time() 的精度约为 16 毫秒。 在 windows 上不准确(没用) time_ns 确实提供纳秒分辨率。但它在 Windows 上真的很精确吗?官方文档中好像没有描述 @Jcyrss, time_ns 不提供纳秒分辨率,它只返回以纳秒为单位的时间。这可能会或可能不会提高分辨率。 Linux 和 Windows 的测量时钟分辨率记录在 PEP 564 中。对于 Windows,time_ns 的测量分辨率为 318 us 在 python >=3.3 中,答案是 time.perf_counter() 和 time.process_time()【参考方案2】:

你也可以使用 time.clock() 它计算 Unix 上进程使用的时间和 Windows 上第一次调用它以来的时间。它比 time.time() 更精确。

这是衡量性能的常用函数。

打电话就行了

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

编辑:Ups,我想念这个问题,因为你想知道确切的时间,而不是花费的时间......

【讨论】:

【参考方案3】:

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

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

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

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

在 Windows 上,时间函数似乎使用最后调用的号码:

>>> 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 在 Windows 7 和 Ubuntu 9.10 上测试)

【讨论】:

肯定不是返回最后一个值,而是你在比时钟分辨率更短的时间内多次调用。 您的 linux 代码与您的 windows 代码不同。 Linux 代码显示两个时间值的元组,而您的 Windows 代码仅显示小数部分和一个增量。类似代码的输出将更容易比较。如果此代码显示任何内容,则说明您的 Linux 机器具有 2.2 微秒 time.time() 分辨率,或者您的 Linux 调用需要一段时间(机器真的很慢,在转换时捕获了计时器等)。我将发布一些代码,展示解决您在此处提出的问题的方法。 为了避免特定平台的代码,使用timeit.default_timer()【参考方案4】:

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)

【讨论】:

【参考方案5】:

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

所以time.time() 在我的 Windows 8 x64 上的分辨率为 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...谢谢分享! 在 Windows 上,time.clock 以高精度测量经过的时间。在 OS X 和 Linux 上,它测量 CPU 时间。从 Python 3.3 开始,deprecated 支持 perf_counter 测量经过时间,process_time 测量 CPU。 这个答案具有误导性。仅仅因为您的窗口在您运行此脚本时将计时器分辨率设置为 1 毫秒,并不能保证另一个进程不能或不会将其设置为更高的分辨率。默认分辨率为 15.6 毫秒,任何进程都可以出现并更改该值。我运行了你的脚本,得到了 15 毫秒的增量,然后我使用了 github.com/tebjan/TimerTool 并将其设置为 1 毫秒,然后再次运行,得到了 1 毫秒的时间增量。小心假设 windows 保持 1 毫秒的计时器分辨率,如果需要,您应该明确并在脚本开始时自行设置。【参考方案6】:

如果可以选择 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 的输出,不是吗? 这应该是现在接受的答案恕我直言。另请参阅PEP-418 和PEP-564 中的计时结果表。后者提到了在 Windows 8 上为 perf_counter 测量的 100ns 精度。【参考方案7】:

comment left by tiho on Mar 27 '14 at 17:21 值得拥有自己的答案:

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

【讨论】:

time.clock 自 3.3 版起已弃用【参考方案8】:

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

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

这些功能与没有_ns后缀的版本类似,但 以 Python int 形式返回纳秒数。

【讨论】:

在 Raspberry Pi 3 上看到这个基准会很有趣。 这些函数与原始函数一样准确(或不准确)。唯一改变的是返回数据的格式:它们不是浮点数,而是整数。例如 1ms 返回 1000000 而不是 0.001。【参考方案9】:

我观察到 time.time() 的分辨率在 Windows 10 专业版和教育版之间是不同的。

在 Windows 10 Professional 机器上,分辨率为 1 毫秒。 在 Windows 10 Education 机器上,分辨率为 16 毫秒。

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

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

【讨论】:

【参考方案10】:
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()

【讨论】:

【参考方案11】:

对于那些停留在 Windows(版本 >= server 2012 或 win 8)和 python 2.7 的用户,

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 function

【讨论】:

不错的答案。调用函数本身需要多少时间?例如,在我的 Linux 机器上,调用 time.time()-time.time() 会打印一个略小于 1e-6 (1 us) 的数字。【参考方案12】:

最初的问题是专门针对 Unix 提出的,但多个答案涉及 Windows,因此在 Windows 上存在误导性信息。 windows默认定时器分辨率为15.6ms you can verify here.

使用来自 cod3monk3y 的稍微修改的脚本,我可以显示默认情况下 Windows 计时器分辨率约为 15 毫秒。我正在使用here 提供的工具来修改分辨率。

脚本:

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 位上收集的。

【讨论】:

【参考方案13】:

在使用“两种不同的方法方法”的同一个 win10 OS 系统上,似乎有一个近似的 “500 ns” 时差。如果您关心纳秒精度,请查看下面的代码。

代码的修改是基于用户cod3monk3yKevin S的代码。

操作系统:python 3.7.3 (default, date, time) [MSC v.1915 64 bit (AMD64)]

def measure1(mean):
    for i in range(1, my_range+1):
        x = time.time()
        
        td = x- samples1[i-1][2]
        if i-1 == 0:
            td = 0
        td = f'td:.6f'
        samples1.append((i, td, x))
        mean += float(td)
        print (mean)
        sys.stdout.flush()
        time.sleep(0.001)
    
    mean = mean/my_range
    
    return mean

def measure2(nr):
    t0 = time.time()
    t1 = t0
    while t1 == t0:
        t1 = time.time()
    td = t1-t0
    td = f'td:.6f'
    return (nr, td, t1, t0)

samples1 = [(0, 0, 0)]
my_range = 10
mean1    = 0.0
mean2    = 0.0

mean1 = measure1(mean1)

for i in samples1: print (i)

print ('...\n\n')

samples2 = [measure2(i) for i in range(11)]

for s in samples2:
    #print(f'time delta: s:.4f seconds')
    mean2 += float(s[1])
    print (s)
    
mean2 = mean2/my_range

print ('\nMean1 : ' f'mean1:.6f')
print ('Mean2 : ' f'mean2:.6f')

measure1 结果:

nr, td, t0
(0, 0, 0)
(1, '0.000000', 1562929696.617988)
(2, '0.002000', 1562929696.6199884)
(3, '0.001001', 1562929696.620989)
(4, '0.001001', 1562929696.62199)
(5, '0.001001', 1562929696.6229906)
(6, '0.001001', 1562929696.6239917)
(7, '0.001001', 1562929696.6249924)
(8, '0.001000', 1562929696.6259928)
(9, '0.001001', 1562929696.6269937)
(10, '0.001001', 1562929696.6279945)
...

measure2 结果:

nr, td , t1, t0
(0, '0.000500', 1562929696.6294951, 1562929696.6289947)
(1, '0.000501', 1562929696.6299958, 1562929696.6294951)
(2, '0.000500', 1562929696.6304958, 1562929696.6299958)
(3, '0.000500', 1562929696.6309962, 1562929696.6304958)
(4, '0.000500', 1562929696.6314962, 1562929696.6309962)
(5, '0.000500', 1562929696.6319966, 1562929696.6314962)
(6, '0.000500', 1562929696.632497, 1562929696.6319966)
(7, '0.000500', 1562929696.6329975, 1562929696.632497)
(8, '0.000500', 1562929696.633498, 1562929696.6329975)
(9, '0.000500', 1562929696.6339984, 1562929696.633498)
(10, '0.000500', 1562929696.6344984, 1562929696.6339984)

最终结果:

Mean1 : 0.001001 #(测量1函数)

Mean2 : 0.000550 # (measure2 函数)

【讨论】:

【参考方案14】:

这是一个基于 Cyber​​Snoopy 上面发布的答案(使用 GetSystemTimePreciseAsFileTime)的 Windows 的 python 3 解决方案。我们从 jfs

中借用了一些代码

Python datetime.utcnow() returning incorrect datetime

并以微秒为单位获得精确的时间戳(Unix 时间)

#! python3
import ctypes.wintypes

def utcnow_microseconds():
    system_time = ctypes.wintypes.FILETIME()
    #system call used by time.time()
    #ctypes.windll.kernel32.GetSystemTimeAsFileTime(ctypes.byref(system_time))
    #getting high precision:
    ctypes.windll.kernel32.GetSystemTimePreciseAsFileTime(ctypes.byref(system_time))
    large = (system_time.dwHighDateTime << 32) + system_time.dwLowDateTime
    return large // 10 - 11644473600000000

for ii in range(5):
    print(utcnow_microseconds()*1e-6)

参考https://docs.microsoft.com/en-us/windows/win32/sysinfo/time-functionshttps://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimepreciseasfiletimehttps://support.microsoft.com/en-us/help/167296/how-to-convert-a-unix-time-t-to-a-win32-filetime-or-systemtime

【讨论】:

以上是关于Python中的高精度时钟的主要内容,如果未能解决你的问题,请参考以下文章

高精度NTP授时服务器,光学时钟来贡献

DS12C887,DS1302,DS3231时钟芯片那个精度比较高?

DS12C887,DS1302,DS3231时钟芯片哪个精度比较高

具有毫秒精度的 Linux/OSX 时钟分辨率?

SylixOS高精度时钟分析

5G网络PTP时钟同步(NTP时间服务器)组网方案研究