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

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

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

【问题讨论】:

标签: python time


【解决方案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-418PEP-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-functions
              https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimepreciseasfiletime
              https://support.microsoft.com/en-us/help/167296/how-to-convert-a-unix-time-t-to-a-win32-filetime-or-systemtime

              【讨论】:

                猜你喜欢
                • 2012-01-25
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2014-09-16
                • 2011-06-30
                • 2011-06-14
                相关资源
                最近更新 更多