如何在 Python 中获取毫秒和微秒分辨率的时间戳?

How can I get millisecond and microsecond-resolution timestamps in Python?

如何在 Python 中获取毫秒和微秒分辨率的时间戳?
我也喜欢类似 Arduino 的 delay()(以毫秒为单位延迟)和 delayMicroseconds() 函数。

我在问这个问题之前阅读了其他答案,但它们依赖于 time 模块,在 Python 3.3 之前没有任何类型的保证分辨率。它的分辨率无处不在。最受好评的答案 here 引用了 16 毫秒的 Windows 分辨率(使用他们的答案),比我在此处提供的答案(0.5 us 分辨率)差 32000 倍 ).同样,我需要 1 ms1 us(或类似)分辨率,not 16000 us分辨率。

  1. my own answer on how to do the same thing (get ms and us-resolution timestamps) in C++

对于 Windows: 这是一个功能齐全的模块,适用于 Linux(也适用于 Python 3.3 之前的版本)和 Windows:

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

  • 微米()
  • 毫秒()
  • 延迟()
  • 延迟微秒()

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

-*****
absolute-timing-cpu-cycle-count
   
LINUX:
-


"""

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 中获取上述毫秒和微秒分辨率的时间戳,请 post,因为这也会很有帮助。

这也适用于 Linux,包括 Python 3.3 之前的版本,因为我通过 ctypes 模块使用 C 函数来读取时间戳。

(注意:上面的代码最初 post 在这里编辑:http://www.electricrcaircraftguy.com/2016/07/arduino-like-millisecond-and-microsecond-timestamps-in-python.html

特别感谢@ArminRonacher 在 Python 3.3 Linux 之前的出色回答:

时间戳和时钟参考:

  1. Windows:QueryPerformanceCounter()https://docs.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancecounter

    Retrieves the current value of the performance counter, which is a high resolution (<1us) time stamp that can be used for time-interval measurements.

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

    CLOCK_MONOTONIC

    A nonsettable system-wide clock that represents monotonic time since—as described by POSIX—"some unspecified point in the past". On Linux, that point corresponds to the number of seconds that the system has been running since it was booted.

    CLOCK_MONOTONIC_RAW (since Linux 2.6.28; Linux-specific)

    Similar to CLOCK_MONOTONIC, but provides access to a raw hardware-based time that is not subject to NTP adjustments or the incremental adjustments performed by adjtime(3). This clock does not count time that the system is suspended.

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

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

但是,我上面的模块为 Python 代码在 Python 3.3 之前以及之后 提供了高分辨率时间戳Linux 和 Windows.

这是我的意思的一个例子,表明 time.sleep() 函数不一定是高分辨率函数。 在我的 Windows 机器上,它的分辨率最多可能是 8ms,而我上面的模块有 0.5us 分辨率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

我的 RASPBERRY PI 版本 1 B+ 的示例结果(请注意,使用 time.sleep 和我的模块之间的结果基本相同......显然 time 中的低级函数已经在这里访问分辨率更高的计时器,因为它是一台 Linux 机器 (运行 Raspbian)...但是在我的 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 高分辨率时间戳,请在此处查看我的回答:
  2. 我的 C 和 C++ Linux 具有 millis()micros()nanos()sleep_ns()sleep_until_ns 的高分辨率时序库use_realtime_scheduler()get_estimated_resolution()
    1. timinglib.h
    2. timinglib.c
  3. [我对 C 和 C++ 的回答,包括微控制器(或任何其他系统)]
  4. [我对 C 和 C++ 的回答,包括微控制器和 Arduino(或任何其他系统)]Full coulomb counter example demonstrating the above concept with timestamp-based, single-threaded, cooperative multi-tasking
  5. [我在 Linux 中对 C 和 C++ 的回答——可以使用 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
import time

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

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

另请参阅:How can I make a time delay in Python?