如何在Python中获取毫秒和微秒级别的时间戳?

18
如何在Python中获得毫秒和微秒级别的时间戳?此外,我还想要类似于Arduino的delay()(以毫秒为单位延迟)和 delayMicroseconds() 函数。阅读其他答案后,我发现它们依赖于time模块,但 Python 3.3之前的模块完全没有任何保证分辨率的类型,其分辨率难以控制。最被赞的答案在这里引用了Windows分辨率(使用他们的答案),为16毫秒,这比我提供的答案(0.5 微秒分辨率)差32000倍。再次强调,我需要1毫秒和1微秒或类似的分辨率,而不是16000微秒的分辨率。

相关:

  1. 我的回答介绍如何在C++中获取相同的结果(获得ms和us分辨率的时间戳)

换句话说,那个问题实际上是:“如何在Python中获得亚秒级时间戳?”,而我的问题实际上是“如何在Python中获得毫秒和微秒级时间戳?”_不是_同一个问题。 - Gabriel Staples
Stack Overflow的问题没有时间限制,而且另一个问题明确要求“高精度”时钟。据我所知,这里的所有内容都可以完全符合那里的主题。在此帖子发布几天前,ereOn在另一个问题上的答案(到目前为止未编辑)被添加了进来,其中明确引用了time.perf_counter,它在Windows上使用了QueryPerformanceCounter - Karl Knechtel
@KarlKnechtel,ereOn的回答需要 Python 3.3 或更高版本。当时我也没有这个版本。我也尝试了那个答案。我的答案在Python 3.3之前的版本中也可以工作。我再重复一遍:那里的_没有一个_答案有效。因此,在2016年,我使用Python _3.3之前的版本_提出了这个新问题,希望有人能告诉我答案,最终我自己解决并在这里发布了问答。现在,其他问题已经改变了。 - Gabriel Staples
@KarlKnechtel,你认为我现在该怎么做?移动我的答案吗?此时此刻我不确定什么是最好的选择,但当时确实需要比现有方案更好的解决方案,所以我按照自己的想法去做了。即使那时候,也引起了一波又一波的争论,因为人们当时并没有理解其中的细微差别,他们告诉我16毫秒分辨率的答案和我0.5微秒分辨率的答案一样好。 - Gabriel Staples
我认为进一步讨论应该在 Meta 上进行。 - Karl Knechtel
显示剩余4条评论
2个回答

27

2022年8月更新:

在现代的Python 3中,先使用import time,然后使用time.monotonic_ns()可能已经足够了。 请参见我对另一个问题的新回答:Python中的高精度时钟 在2016年我回答这个问题时,使用树莓派上的Python 3.1时,这个功能并不存在。

请参见https://docs.python.org/3/library/time.html#time.monotonic_ns。这是Python 3.7中新增的功能。不过我自己还没有测试过。感谢@HenrikMadsen在他的回答中发布了这个链接here,但他不幸地删除了它。

我仍然需要测试这些新的Python 3.7及其后续版本的函数,以确定它们是否与我下面所做的一样好。

因此,请先尝试这个,并将其与我下面所做的进行比较:

import time

time_ns = time.monotonic_ns()

你也可以尝试在Unix或Linux系统上使用time.clock_gettime_ns()。根据其名称,它似乎调用了底层的clock_gettime() C函数,我在C中使用它来实现nanos()函数(在这里),并在我的C Unix / Linux库中使用:timinglib.c

2016年的原始回答:

这是一个完全功能的模块,适用于LinuxWindows,并且与其他所有答案链接在此处不同之处在于它适用于 Python 3.3之前的版本 。所有其他答案在大多数情况下需要Python 3.7或更高版本,在其他情况下需要Python 3.3或更高版本。同样,我的下面的答案适用于任何版本的Python的Windows和Linux,至少可以追溯到Python 3.0左右(我不记得它是否适用于Python 2.7)。

它使用 ctypes 库通过Windows中的.dll“动态链接库”文件或Unix或Linux中的.so“共享对象”库文件在Python中调用C或C ++动态库。

函数和代码示例。
函数包括:

  • micros()
  • millis()
  • delay()
  • delayMicroseconds()

从我的eRCaGuy_PyTime存储库下载GS_timing.py,然后执行以下操作:

import GS_timing

time_ms = GS_timing.millis()
time_us = GS_timing.micros()
GS_timing.delay(10)                # delay 10 ms
GS_timing.delayMicroseconds(10000) # delay 10000 us

Python代码模块(在GitHub上为eRCaGuy_PyTime):

"""
GS_timing.py
-create some low-level Arduino-like millis() (milliseconds) and micros() 
 (microseconds) timing functions for Python 
By Gabriel Staples
http://www.ElectricRCAircraftGuy.com 
-click "Contact me" at the top of my website to find my email address 
Started: 11 July 2016 
Updated: 13 Aug 2016 

History (newest on top): 
20160813 - v0.2.0 created - added Linux compatibility, using ctypes, so that it's compatible with pre-Python 3.3 (for Python 3.3 or later just use the built-in time functions for Linux, shown here: https://docs.python.org/3/library/time.html)
-ex: time.clock_gettime(time.CLOCK_MONOTONIC_RAW)
20160711 - v0.1.0 created - functions work for Windows *only* (via the QPC timer)

References:
WINDOWS:
-personal (C++ code): GS_PCArduino.h
1) Acquiring high-resolution time stamps (Windows)
   -https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx
2) QueryPerformanceCounter function (Windows)
   -https://msdn.microsoft.com/en-us/library/windows/desktop/ms644904(v=vs.85).aspx
3) QueryPerformanceFrequency function (Windows)
   -https://msdn.microsoft.com/en-us/library/windows/desktop/ms644905(v=vs.85).aspx
4) LARGE_INTEGER union (Windows)
   -https://msdn.microsoft.com/en-us/library/windows/desktop/aa383713(v=vs.85).aspx

-*****https://dev59.com/gVLTa4cB1Zd3GeqPcJ9J
absolute-timing-cpu-cycle-count
   
LINUX:
-https://dev59.com/THM_5IYBdhLWcg3w1G-N


"""

import ctypes, os 

#Constants:
VERSION = '0.2.0'

#-------------------------------------------------------------------
#FUNCTIONS:
#-------------------------------------------------------------------
#OS-specific low-level timing functions:
if (os.name=='nt'): #for Windows:
    def micros():
        "return a timestamp in microseconds (us)"
        tics = ctypes.c_int64()
        freq = ctypes.c_int64()

        #get ticks on the internal ~2MHz QPC clock
        ctypes.windll.Kernel32.QueryPerformanceCounter(ctypes.byref(tics)) 
        #get the actual freq. of the internal ~2MHz QPC clock
        ctypes.windll.Kernel32.QueryPerformanceFrequency(ctypes.byref(freq))  
        
        t_us = tics.value*1e6/freq.value
        return t_us
        
    def millis():
        "return a timestamp in milliseconds (ms)"
        tics = ctypes.c_int64()
        freq = ctypes.c_int64()

        #get ticks on the internal ~2MHz QPC clock
        ctypes.windll.Kernel32.QueryPerformanceCounter(ctypes.byref(tics)) 
        #get the actual freq. of the internal ~2MHz QPC clock 
        ctypes.windll.Kernel32.QueryPerformanceFrequency(ctypes.byref(freq)) 
        
        t_ms = tics.value*1e3/freq.value
        return t_ms

elif (os.name=='posix'): #for Linux:

    #Constants:
    CLOCK_MONOTONIC_RAW = 4 # see <linux/time.h> here: https://github.com/torvalds/linux/blob/master/include/uapi/linux/time.h
    
    #prepare ctype timespec structure of {long, long}
    class timespec(ctypes.Structure):
        _fields_ =\
        [
            ('tv_sec', ctypes.c_long),
            ('tv_nsec', ctypes.c_long)
        ]
        
    #Configure Python access to the clock_gettime C library, via ctypes:
    #Documentation:
    #-ctypes.CDLL: https://docs.python.org/3.2/library/ctypes.html
    #-librt.so.1 with clock_gettime: https://docs.oracle.com/cd/E36784_01/html/E36873/librt-3lib.html #-
    #-Linux clock_gettime(): http://linux.die.net/man/3/clock_gettime
    librt = ctypes.CDLL('librt.so.1', use_errno=True)
    clock_gettime = librt.clock_gettime
    #specify input arguments and types to the C clock_gettime() function
    # (int clock_ID, timespec* t)
    clock_gettime.argtypes = [ctypes.c_int, ctypes.POINTER(timespec)]

    def monotonic_time():
        "return a timestamp in seconds (sec)"
        t = timespec()
        #(Note that clock_gettime() returns 0 for success, or -1 for failure, in
        # which case errno is set appropriately)
        #-see here: http://linux.die.net/man/3/clock_gettime
        if clock_gettime(CLOCK_MONOTONIC_RAW , ctypes.pointer(t)) != 0:
            #if clock_gettime() returns an error
            errno_ = ctypes.get_errno()
            raise OSError(errno_, os.strerror(errno_))
        return t.tv_sec + t.tv_nsec*1e-9 #sec 
    
    def micros():
        "return a timestamp in microseconds (us)"
        return monotonic_time()*1e6 #us 
        
    def millis():
        "return a timestamp in milliseconds (ms)"
        return monotonic_time()*1e3 #ms 

#Other timing functions:
def delay(delay_ms):
    "delay for delay_ms milliseconds (ms)"
    t_start = millis()
    while (millis() - t_start < delay_ms):
      pass #do nothing 
    return

def delayMicroseconds(delay_us):
    "delay for delay_us microseconds (us)"
    t_start = micros()
    while (micros() - t_start < delay_us):
      pass #do nothing 
    return 
        
#-------------------------------------------------------------------
#EXAMPLES:
#-------------------------------------------------------------------
#Only executute this block of code if running this module directly,
#*not* if importing it
#-see here: http://effbot.org/pyfaq/tutor-what-is-if-name-main-for.htm
if __name__ == "__main__": #if running this module as a stand-alone program

    #print loop execution time 100 times, using micros()
    tStart = micros() #us
    for x in range(0, 100):
        tNow = micros() #us
        dt = tNow - tStart #us; delta time 
        tStart = tNow #us; update 
        print("dt(us) = " + str(dt))

    #print loop execution time 100 times, using millis()
    print("\n")
    tStart = millis() #ms
    for x in range(0, 100):
        tNow = millis() #ms
        dt = tNow - tStart #ms; delta time 
        tStart = tNow #ms; update 
        print("dt(ms) = " + str(dt))
        
    #print a counter once per second, for 5 seconds, using delay 
    print("\nstart")
    for i in range(1,6):
        delay(1000)
        print(i)

    #print a counter once per second, for 5 seconds, using delayMicroseconds
    print("\nstart")
    for i in range(1,6):
        delayMicroseconds(1000000)
        print(i)

如果您知道如何在Linux中获取上述毫秒和微秒分辨率的时间戳,请发布,因为那将非常有帮助。
这也适用于Linux,包括Python 3.3之前的版本,因为我使用ctypes模块通过C函数读取时间戳。
(注意:以上代码最初发布在此处:http://www.electricrcaircraftguy.com/2016/07/arduino-like-millisecond-and-microsecond-timestamps-in-python.html
特别感谢@ArminRonacher在此处提供的Python 3.3之前Linux的杰出答案:https://dev59.com/THM_5IYBdhLWcg3w1G-N#1205762 时间戳和时钟参考:
  1. Windows: QueryPerformanceCounter(): https://learn.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancecounter:

    检索性能计数器的当前值,该计数器是一个高分辨率(<1us)时间戳,可用于时间间隔测量。

  2. Linux: clock_gettime(): https://man7.org/linux/man-pages/man3/clock_gettime.3.html (强调添加):

    CLOCK_MONOTONIC

    表示自某个未指定的时间点以来的单调时间的不可设置系统范围时钟。在Linux上,该点对应于系统自启动以来运行的秒数。

    CLOCK_MONOTONIC_RAW (since Linux 2.6.28; 仅限Linux)

    类似于CLOCK_MONOTONIC,但提供对未经NTP调整或由adjtime(3)执行的增量调整不受影响的原始基于硬件的时间的访问。此时钟不计算系统挂起的时间。

  3. 请注意,两个系统上的两个时钟均不提供“墙钟”类型的时间戳。相反,它们都提供高分辨率(亚微秒)时间戳,通常计算自启动以来的时间。这些时间戳可用于精确测量事件的时间、生成可重复的周期循环以及在代码中测量小的时间间隔,具有极高的分辨率、精度和准确性。

更新:在 Python 3.3 之前,内置的 Python 时间库(https://docs.python.org/3.5/library/time.html)没有任何显式的高分辨率函数。然而,现在它提供了其他选项,包括一些高分辨率函数。

然而,我的模块为 Python 3.3 之前和之后的代码提供了高分辨率时间戳,并且在 Linux 和 Windows 上都能实现,而且

这里是一个例子,展示了 time.sleep() 函数不一定是高分辨率函数。在我的 Windows 机器上,它的分辨率可能最多只有8毫秒,而我的模块在同一台机器上具有0.5微秒的分辨率比原来好了16000倍!)。

代码演示:

import time
import GS_timing as timing

def delayMicroseconds(n):
    time.sleep(n / 1000000.)

def delayMillisecond(n):
    time.sleep(n / 1000.)

t_start = 0
t_end = 0

#using time.sleep
print('using time.sleep')
print('delayMicroseconds(1)')
for x in range(10):
    t_start = timing.micros() #us 
    delayMicroseconds(1)
    t_end = timing.micros() #us
    print('dt (us) = ' + str(t_end - t_start))
print('delayMicroseconds(2000)')
for x in range(10):
    t_start = timing.micros() #us 
    delayMicroseconds(2000)
    t_end = timing.micros() #us
    print('dt (us) = ' + str(t_end - t_start))
  
#using GS_timing
print('\nusing GS_timing')
print('timing.delayMicroseconds(1)')
for x in range(10):
    t_start = timing.micros() #us 
    timing.delayMicroseconds(1)
    t_end = timing.micros() #us
    print('dt (us) = ' + str(t_end - t_start))
print('timing.delayMicroseconds(2000)')
for x in range(10):
    t_start = timing.micros() #us 
    timing.delayMicroseconds(2000)
    t_end = timing.micros() #us
    print('dt (us) = ' + str(t_end - t_start))

我的Windows 8.1电脑的样本结果(注意time.sleep的表现更差):

using time.sleep
delayMicroseconds(1)
dt (us) = 2872.059814453125
dt (us) = 886.3939208984375
dt (us) = 770.4649658203125
dt (us) = 1138.7698974609375
dt (us) = 1426.027099609375
dt (us) = 734.557861328125
dt (us) = 10617.233642578125
dt (us) = 9594.90576171875
dt (us) = 9155.299560546875
dt (us) = 9520.526611328125
delayMicroseconds(2000)
dt (us) = 8799.3056640625
dt (us) = 9609.2685546875
dt (us) = 9679.5439453125
dt (us) = 9248.145263671875
dt (us) = 9389.721923828125
dt (us) = 9637.994262695312
dt (us) = 9616.450073242188
dt (us) = 9592.853881835938
dt (us) = 9465.639892578125
dt (us) = 7650.276611328125

using GS_timing
timing.delayMicroseconds(1)
dt (us) = 53.3477783203125
dt (us) = 36.93310546875
dt (us) = 36.9329833984375
dt (us) = 34.8812255859375
dt (us) = 35.3941650390625
dt (us) = 40.010986328125
dt (us) = 38.4720458984375
dt (us) = 56.425537109375
dt (us) = 35.9072265625
dt (us) = 36.420166015625
timing.delayMicroseconds(2000)
dt (us) = 2039.526611328125
dt (us) = 2046.195068359375
dt (us) = 2033.8841552734375
dt (us) = 2037.4747314453125
dt (us) = 2032.34521484375
dt (us) = 2086.2059326171875
dt (us) = 2035.4229736328125
dt (us) = 2051.32470703125
dt (us) = 2040.03955078125
dt (us) = 2027.215576171875

在我的树莓派1B+上进行的样本结果(请注意,使用time.sleep和我的模块之间的结果基本相同...显然,time中的低级函数已经访问了更好的分辨率计时器,因为它是运行Raspbian的Linux机器...但是,在我的GS_timing模块中,我明确调用了CLOCK_MONOTONIC_RAW计时器。否则将使用什么计时器无从得知):

using time.sleep
delayMicroseconds(1)
dt (us) = 1022.0
dt (us) = 417.0
dt (us) = 407.0
dt (us) = 450.0
dt (us) = 2078.0
dt (us) = 393.0
dt (us) = 1297.0
dt (us) = 878.0
dt (us) = 1135.0
dt (us) = 2896.0
delayMicroseconds(2000)
dt (us) = 2746.0
dt (us) = 2568.0
dt (us) = 2512.0
dt (us) = 2423.0
dt (us) = 2454.0
dt (us) = 2608.0
dt (us) = 2518.0
dt (us) = 2569.0
dt (us) = 2548.0
dt (us) = 2496.0

using GS_timing
timing.delayMicroseconds(1)
dt (us) = 572.0
dt (us) = 673.0
dt (us) = 1084.0
dt (us) = 561.0
dt (us) = 728.0
dt (us) = 576.0
dt (us) = 556.0
dt (us) = 584.0
dt (us) = 576.0
dt (us) = 578.0
timing.delayMicroseconds(2000)
dt (us) = 2741.0
dt (us) = 2466.0
dt (us) = 2522.0
dt (us) = 2810.0
dt (us) = 2589.0
dt (us) = 2681.0
dt (us) = 2546.0
dt (us) = 3090.0
dt (us) = 2600.0
dt (us) = 2400.0

相关:

  1. 我的3个时间戳函数集(相互交叉链接):
    1. 关于C时间戳,请参见我在这里的答案:Get a timestamp in C in microseconds?
    2. 关于C++高分辨率时间戳,请参见我在这里的答案:Getting an accurate execution time in C++ (micro seconds)
    3. 关于Python高分辨率时间戳,请参见我在这里的答案:How can I get millisecond and microsecond-resolution timestamps in Python?
  2. 我的C和C++ Linux高分辨率计时库,包括millis()micros()nanos()sleep_ns()sleep_until_nsuse_realtime_scheduler()get_estimated_resolution()等。
    1. timinglib.h
    2. timinglib.c
  3. [我的C和C++答案,包括微控制器(或任何其他系统)] How to do timestamp-based, non-blocking, single-threaded cooperative multi-tasking
  4. [我的C和C++答案,包括微控制器和Arduino(或任何其他系统)] Full coulomb counter example demonstrating the above concept with timestamp-based, single-threaded, cooperative multi-tasking
  5. [我的C和C++答案,在Linux中--可以使用ctypes模块轻松适应Python,如上所示] How to run a high-resolution, high-precision periodic loop in Linux easily, at any frequency (ex: up to 10 KHz~100 KHz) using a soft real-time scheduler and nanosecond delays

1
顺便说一下,它现在在Darwin上崩溃了。 - alanjds
1
我想这并不让我感到意外;低级时间计时很大程度上依赖于操作系统的内部工作原理,而Darwin既不是Linux也不是基于Windows的。如果您能在Darwin上获得类似的东西,并且具有类似的分辨率,请告诉我,我可以将其纳入我的代码中。 - Gabriel Staples
在Windows中,它返回的是系统运行时间而不是时间戳。 - Smart Manoj
1
@brec,你使用的是哪个版本的Python?在现代的Python 3中,import time后跟time.monotonic_ns()可能已经足够了。在我回答的时候,这还不存在。请参见https://docs.python.org/3/library/time.html#time.monotonic_ns。这是Python 3.7中的新功能。不过我自己还没有测试过。感谢@HenrikMadsen在他的答案中发布了这个问题(https://stackoverflow.com/a/68486952/4561887),但很遗憾他已经删除了它。 - Gabriel Staples
1
Gabriel,我正在使用3.10.5版本。在我之前的评论后,我使用了time.perf_counter_ns——在MacOS上与time.monotonic_ns相同。但是,我原以为我正在使用不如你的代码准确的东西。感谢提供信息! - brec
显示剩余8条评论

-5

time.sleep()在我的Windows机器上分辨率非常低(8毫秒),而我在下面的代码中所做的分辨率非常高(0.5微秒)。因此,我的整个问题的前提是“毫秒和微秒分辨率”的时间戳。我刚刚对比了我的模块和time.sleep,结果非常不同。我将把它附加到我的答案中供您查看。 - Gabriel Staples
我花了大约12个小时的时间来解决这个时间问题,因为我是Python的新手。 - Gabriel Staples
我怀疑有很多人(就像你一样)甚至没有意识到我所做的事情有什么不同或更好,或者为什么我不使用内置的 time 库。嗯……这并不是那么简单。如果你阅读了时间文档 (https://docs.python.org/3.5/library/time.html) ,你也会看到在 Python 3.3 发布时对内置 Python 库进行了一些 重大 更改,但我的树莓派只有 Python 3.2.3,并且显示它是最新版本,所以我必须定制一些东西来确保我得到想要的东西。例如:CLOCK_MONOTONIC_RAW - Gabriel Staples

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