程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

Python timeit庫測試代碼片段運行時間

編輯:Python

timeit庫文檔:https://docs.python.org/zh-cn/3/library/timeit.html


timeit 是一個用來測量小代碼片段執行時間的工具庫,有命令行函數調用兩種方法可供使用。

可以直接查看文檔最後的示例部分快速了解其用法。
注意

  • 使用函數調用時,timeit()方法的返回值以秒為單位,而且返回的是代碼語句運行number次(timeit方法的參數)的總耗時,不是多次運行取最好結果/平均結果。
  • 在在命令行中,使用timeit語句返回結果是多次循環所花費的平均時間,時間單位可以指定。

基本示例:

需要注意的是,在命令行,傳給timeit 的 stmt 參數(即要運行的測試代碼)都要是字符串格式的,而不是函數引用或Python語句。在函數中,除了字符串,可以傳入一個可調用對象。

  1. 命令行方式:
    如果是在windows下使用命令行執行語句,切記最外層需要使用雙引號,否則會報錯。

    $ python3 -m timeit '"-".join(str(n) for n in range(100))'
    10000 loops, best of 5: 30.2 usec per loop
    $ python3 -m timeit '"-".join([str(n) for n in range(100)])'
    10000 loops, best of 5: 27.5 usec per loop
    $ python3 -m timeit '"-".join(map(str, range(100)))'
    10000 loops, best of 5: 23.2 usec per loop
    
  2. 函數調用:

    >>> import timeit
    >>> timeit.timeit('"-".join(str(n) for n in range(100))', number=10000)
    0.3018611848820001
    >>> timeit.timeit('"-".join([str(n) for n in range(100)])', number=10000)
    0.2727368790656328
    >>> timeit.timeit('"-".join(map(str, range(100)))', number=10000)
    0.23702679807320237
    

    在函數調用時,可以傳入可調用對象:

    >>> timeit.timeit(lambda: "-".join(map(str, range(100))), number=10000)
    0.19665591977536678
    

但是請注意,timeit ()只有在使用命令行界面時才會自動確定重復次數。


Python接口(函數調用)的使用方法:

模塊包含3個常用方法和一個Timer類:

  • timeit.timeit(stmt='pass', setup='pass', timer=<default timer>, number=1000000, globals=None)
    使用給定的stmt、setup代碼和timer函數創建一個 Timer 實例,並根據number運行它的 timeit ()方法。可選的 globals 參數指定在其中執行代碼的命名空間。

  • timeit.repeat(stmt='pass', setup='pass', timer=<default timer>, repeat=5, number=1000000, globals=None)
    使用給定的stmt和setup代碼和timer函數創建一個 Timer 實例,並使用給定的repeat計數和number執行運行它的 repeat ()方法(默認執行5 * 1000000次)。可選的 globals 參數指定在其中執行代碼的命名空間。

  • timeit.default_timer()
    默認的計時器 time.perf_counter().

  • *class* timeit.Timer(stmt='pass', setup='pass', timer=<timer function>, globals=None)
    用於計時小代碼端執行速度的類。該類包括timeit(), autorange(), repeat()和print_exec()四個方法。

    構造函數接受一條要計時的語句stmt、一條用於設置的附加語句setup和一個計時器函數。這兩個語句都默認為“ pass”; 計時器函數依賴於平台。Stmt 和 setup 還可以包含由; 或換行分隔的多個語句,只要它們不包含多行字符串文字。默認情況下,語句將在其名稱空間內執行; 可以通過將名稱空間傳遞給globals變量來控制此行為。

    若要度量第一個語句stmt的執行時間,請使用 timeit ()方法。Repeat ()和 autOrange ()方法是方便多次調用 timeit ()的方法。setup語句的執行時間被排除在整個計時執行運行之外。

    Stmt 和 setup 參數還可以接受不帶參數的可調用對象(callable)。這將把對它們的調用嵌入到一個計時器函數中,然後由 timeit ()執行。注意,在這種情況下,由於額外的函數調用,計時開銷有點大。

    • timeit(number=1000000)
      主語句的執行時間。這會執行 setup 語句一次,然後返回執行主語句若干次所需的時間,以秒為單位作為浮點數進行計算。參數是通過循環的次數,默認為100萬次。主語句、 setup 語句和要使用的 timer 函數被傳遞給構造函數。

      注意:在使用timeit()函數測試時,默認會臨時關閉Python的垃圾回收功能。這種方法的優點是它使獨立計時更具可比性。缺點是 GC 可能是被測量函數性能的重要組成部分。如果是這樣,GC 可以作為設置字符串中的第一條語句重新啟用。 timeit.Timer('for i in range(10): oct(i)', 'gc.enable()').timeit() 在創建Timer對象時的stmt中設置gc.enable()

    • autorange(callback=None)
      自動確定調用 timeit ()的次數。
      這是一個便捷函數,用來反復調用 timeit () ,使總時間 > = 0.2秒,返回最終結果(循環次數和循環花費的時間)。它使用序列1、2、5、10.20、50中的遞增數字調用 timeit () ,直到所花費的時間至少為0.2秒。
      如果回調callback是給定的,並且不是 None,那麼在每次試驗之後將使用兩個參數調用它: callback (number,time_taken)

    • repeat(repeat=5, number=1000000)
      調用計時器repeat次。
      這是一個便捷函數,它重復調用 timeit () ,返回一個結果列表。第一個參數repeat指定調用 timeit ()的次數。第二個參數指定 timeit ()的 number 參數。
      注意:從結果向量計算平均值和標准差並報告這些結果是很有誘惑力的。然而,這並不是很有用。在一個典型的例子中,最低值給出了您的計算機運行給定代碼片段的速度的下限; 結果向量中較高的值通常不是由 Python 速度的變化引起的,而是由於其他進程干擾了計時的准確性。因此,結果的 min ()可能是您應該感興趣的唯一數字。之後,您應該用常識看待整個向量而不是從統計的角度看待整個結果。

    • print_exc(file=None)
      從計時代碼打印回溯的幫助程序。

      t = Timer(...) # outside the try/except
      try:
      t.timeit(...) # or t.repeat(...)
      except Exception:
      t.print_exc()
      

      相對於標准回溯的優勢在於,將顯示已編譯模板中的源代碼行。可選的 file 參數指向發送回溯的位置; 它默認為 sys.stderr。


命令行接口:

python -m timeit [-n N] [-r N] [-u U] [-s S] [-h] [statement ...]
-n timer執行statement的次數
-r 重復執行timer的次數,默認為5
-u 指定timer輸出的時間格式,可以指定為:nsec、usec、msec、sec
-s setup 語句,默認為pass
-p 使用time.process_time()而不是默認的time.perf_counter()來度量進程時間,而不是牆鐘時間
-v 詳細信息,打印原始的計時結果
-h 幫助信息

一個多行的statement可以通過使用 ; 分給為多個語句,帶縮進的行可以使用引號括起一個參數並使用前導空格。-s選項的多行語法是類似的。


示例:

$ python -m timeit -s 'text = "sample string"; char = "g"' 'char in text'
5000000 loops, best of 5: 0.0877 usec per loop
$ python -m timeit -s 'text = "sample string"; char = "g"' 'text.find(char)'
1000000 loops, best of 5: 0.342 usec per loop

使用-s 指定的setup語句只在開始時執行一次。
在輸出中有三個字段:

  • loop count:告訴您每次計時循環重復運行語句體的次數。
  • repetition count:(best of 5)告訴您計時循環重復的次數。
  • 平均耗時:語句主體在計時循環的最佳重復范圍內所花費的平均時間。

以上語句使用函數和Timer類的代碼如下:

>>> import timeit
>>> timeit.timeit('char in text', setup='text = "sample string"; char = "g"')
0.41440500499993504
>>> timeit.timeit('text.find(char)', setup='text = "sample string"; char = "g"')
1.7246671520006203
>>> import timeit
>>> t = timeit.Timer('char in text', setup='text = "sample string"; char = "g"')
>>> t.timeit()
0.3955516149999312
>>> t.repeat()
[0.40183617287970225, 0.37027556854118704, 0.38344867356679524, 0.3712595970846668, 0.37866875250654886]

多行語句:

當statement語句中包含多行語句時,在命令行和Python代碼中可以如下執行:

$ python -m timeit 'try:' ' str.__bool__' 'except AttributeError:' ' pass'
20000 loops, best of 5: 15.7 usec per loop
$ python -m timeit 'if hasattr(str, "__bool__"): pass'
50000 loops, best of 5: 4.26 usec per loop
$ python -m timeit 'try:' ' int.__bool__' 'except AttributeError:' ' pass'
200000 loops, best of 5: 1.43 usec per loop
$ python -m timeit 'if hasattr(int, "__bool__"): pass'
100000 loops, best of 5: 2.23 usec per loop
>>> import timeit
>>> # attribute is missing
>>> s = """\ ... try: ... str.__bool__ ... except AttributeError: ... pass ... """
>>> timeit.timeit(stmt=s, number=100000)
0.9138244460009446
>>> s = "if hasattr(str, '__bool__'): pass"
>>> timeit.timeit(stmt=s, number=100000)
0.5829014980008651
>>>
>>> # attribute is present
>>> s = """\ ... try: ... int.__bool__ ... except AttributeError: ... pass ... """
>>> timeit.timeit(stmt=s, number=100000)
0.04215312199994514
>>> s = "if hasattr(int, '__bool__'): pass"
>>> timeit.timeit(stmt=s, number=100000)
0.08588060699912603

測試函數運行時間:

當要對我們定義的函數進行時間測量時,可以通過setup參數傳遞腳本中需要的 import 語句(即使是在當前腳本下也要import:from __main__ import func),stmt 參數傳遞方法調用字符串化後的表達式:

def test():
"""Stupid test function"""
L = [i for i in range(100)]
if __name__ == '__main__':
import timeit
print(timeit.timeit("test()", setup="from __main__ import test"))

另一種方法是向globals參數傳遞 globals()函數。這將導致代碼在當前全局命名空間內執行。這可能比單獨指定進口更方便:

def f(x):
return x**2
def g(x):
return x**4
def h(x):
return x**8
import timeit
print(timeit.timeit('[func(42) for func in (f,g,h)]', globals=globals()))

函數調用與命令行調用返回結果的差異:

  • 函數調用中timeit()方法的返回值,單位為秒,而且返回的是運行number次的總用時,不是平均用時。
  • 命令行中返回的結果是循環的平均用時,單位可以自行指定。
In [2]: def test():
...: a = 0
...: for _ in range(1000):
...: a += _
...:
In [3]: timeit.timeit('test()', globals=globals())
Out[3]: 35.442787599982694
In [4]: timeit.timeit('test()', number=1, globals=globals())
Out[4]: 3.50000336766243e-05
In [5]: timeit.timeit('test()', number=10, globals=globals())
Out[5]: 0.00033359997905790806
In [6]: timeit.timeit('test()', number=100, globals=globals())
Out[6]: 0.0032857999904081225
In [7]: timeit.timeit('test()', number=1000, globals=globals())
Out[7]: 0.03538430004846305
>python -m timeit "a=0" "for _ in range(1000):" " a+=_"
10000 loops, best of 5: 32.4 usec per loop
# 以秒為單位
>python -m timeit -u sec "a=0" "for _ in range(1000):" " a+=_"
10000 loops, best of 5: 3.24e-05 sec per loop

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved