咱們能夠經過查看程序核心算法的代碼,得知核心算法的漸進上界或者下界,從而大概估計出程序在運行時的效率,可是這並不夠直觀,也不必定十分靠譜(在總體程序中仍有一些不可忽略的運行細節在估計時被忽略了),所以在實際評測程序時咱們仍是須要實際的考量程序的運行時間和瓶頸,最好具體到執行一段代碼多少次,執行一段代碼花了多少時間,幸虧的是Python自帶了許多有用的工具,能夠幫助咱們實現這些要求,下面是一些我在學習中記錄的筆記,從簡單到複雜介紹了python性能分析的方法,但願個人筆記能幫到您。html
注:寫做不易,轉載請註明出處,謝謝支持~python
timeit能夠在命令行經過-m指令導入做爲腳本運行,也能夠在代碼內import導入使用,它會將代碼執行多遍,而後得出耗時最短的時間是多少,下面是具體的幾種使用方式:
注:我在實際測試時使用的是python3.7的環境,在文末,有舉例說明python2和python3.7使用時實際上不一樣的地方git
1.1 經過在命令行編譯時添加-m
指令,來進行timeit的導入,後面跟着一段字符串,包含用來測試的表達式。(結果中的usec爲微秒)
您也能夠直接在字符串中包含你要測試的模塊名,python會直接執行它,並用timeit得出時間:
這裏在命令行的工做目錄下,有一個test_timeit.py文件,其中的內容以下:算法
def factorial(n): if n == 1: return 1 return n * factorial(n-1) if __name__ == "__main__": factorial(30)
timeit計算了執行main的時間windows
1.2 經過添加 -n N
命令能夠設置語句執行的次數:
ide
1.3 經過添加-r N
設置計時器重複多少次(默認是5次)(最後的結果是取平均?):
函數
1.4 經過添加-s str
設置str語句只在初始化的時候執行一遍,後面會pass這個語句:
圖片過小,把命令寫一下:
python -m timeit -n 100 -r 5 -s "from test_timeit import factorial" "factorial(20)"
其中-s
後跟着的"from test_timeit import factorial"
只在第一次時執行了
其中的factorial是一個計算階乘的小函數,代碼以下:
def factorial(n): if n == 1: return 1 return n * factorial(n-1)
工具
1.5 經過添加-t
使用time.time()
(default on Unix)oop
time.time()
返回從紀元(1970.1.1)至今的秒數。雖然這個函數的返回值永遠爲浮點數,
但並非全部系統提供的秒數都會精確到小數點之後。
通常狀況下這個函數的返回值不會小於它上一次被調用的返回值,除非系統時鐘在兩次調用之間發生了重置。
//參考https://www.cnblogs.com/cuixiaochen/p/4722387.html性能
1.6 經過添加-c
使用 time.clock()
(default on Windows)
time.clock()
在Unix 中,將當前的處理器時間以浮點數的形式返回,單位爲秒。
它的精確度(準確地說是「處理器時間」的精確度)取決於同名的C函數,
不管如何,這個函數是python關於時間計算的標尺。
WINDOWS中,第一次調用,返回的是進程運行的實際時間。
而第二次以後的調用是自第一次調用之後到如今的運行時間。
(其實是以WIN32上QueryPerformanceCounter()爲基礎,它比毫秒錶示更爲精確)
在windows中,time.clock()更準確。//參考https://www.cnblogs.com/cuixiaochen/p/4722387.html
在windows中使用-t
和-c
: 能夠發現兩者是有區別的,這個選項徹底能夠遵循默認,timeit會自動使用更精確的時間計算方法
1.7 經過添加-v
,打印原始計時結果,以得到更高的數字精度,而且顯示更具體的結果
1.8 經過-u
,設置計時單位:
可選項包括:nsec(納秒),usec(微秒),msec(毫秒),sec(秒)
1.9 經過添加-p計算處理時間,而不是wallclock(從測試開始到結束所用的時間,以及 CPU 時間,即 CPU 上總的處理時間)
1.10 執行多行代碼,只需在後面按順序添加多個表達式便可:
1.11 經過添加-v
打印幫助:python -m timeit -h
,打印的內容以下:
C:\python37\mypython\learning test 2019-03>python -m timeit -h Tool for measuring execution time of small code snippets. This module avoids a number of common traps for measuring execution times. See also Tim Peters' introduction to the Algorithms chapter in the Python Cookbook, published by O'Reilly. Library usage: see the Timer class. Command line usage: python timeit.py [-n N] [-r N] [-s S] [-p] [-h] [--] [statement] Options: -n/--number N: how many times to execute 'statement' (default: see below) -r/--repeat N: how many times to repeat the timer (default 5) -s/--setup S: statement to be executed once initially (default 'pass'). Execution time of this setup statement is NOT timed. -p/--process: use time.process_time() (default is time.perf_counter()) -v/--verbose: print raw timing results; repeat for more digits precision -u/--unit: set the output time unit (nsec, usec, msec, or sec) -h/--help: print this usage message and exit --: separate options from statement, use when statement starts with - statement: statement to be timed (default 'pass') A multi-line statement may be given by specifying each line as a separate argument; indented lines are possible by enclosing an argument in quotes and using leading spaces. Multiple -s options are treated similarly. If -n is not given, a suitable number of loops is calculated by trying successive powers of 10 until the total time is at least 0.2 seconds. Note: there is a certain baseline overhead associated with executing a pass statement. It differs between versions. The code here doesn't try to hide it, but you should be aware of it. The baseline overhead can be measured by invoking the program without arguments. Classes: Timer Functions: timeit(string, string) -> float repeat(string, string) -> list default_timer() -> float
2.1 使用timeit.timeit
timeit.timeit(stmt='pass', setup='pass', timer=, number=1000000, globals=None)
參數解釋:
;
分開time.perf_counter()
使用實例:
Q&A: 爲何不帶number參數會比number=10**5時所費時間更長呢?
Q&A: 輸出結果的默認單位是什麼?
經過setup參數,設置初始執行的語句,下例是'x = 2',當重複執行多遍時,'x = 2'只在第一次執行一次。
經過globals攜帶全局變量
執行多個語句
2.2 使用timeit.repeat,輸出的是個列表,包含重複次數個的測試時間
timeit.repeat(stmt='pass', setup='pass', timer=, repeat=3, number=1000000, globals=None)
參數解釋:
;
分開time.perf_counter()
使用起來與timeit.timeit相似,只是多了repeat參數,下例將timeit.repeat和timeit.timeit進行了對比,可見repeat只是重複了屢次而已
2.3 使用timeit.default_timer(),獲取計時器,默認是 time.perf_counter(),示例:
備註:
兩個計時器—— time.perf_counter()和time.process_time()
time.perf_counter()
返回性能計數器的值(以小數秒爲單位),即具備最高分辨率的時鐘測量一段很短的時間。它確實包括睡眠期間通過的時間,而且是全系統的.返回值的引用點未定義,所以只有連續調用的結果之間的差別是有效的。
time.process_time():
返回系統和當前進程的用戶CPU時間之和的值(以小數秒爲單位)。它不包括睡眠期間通過的時間。它的定義是全過程的。
返回值的引用點未定義,所以只有連續調用的結果之間的差別是有效的。
在一些背景下,「時間」有兩種不一樣的類型:絕對時間和相對時間。
絕對時間是「真實世界時間」,由time.time()咱們都習慣於處理這個問題。它一般是從過去的一個不動點(例如,01/01/1970 UTC的UNIX時代)以致少1秒的分辨率來測量的。現代系統一般提供毫秒或微秒分辨率。它由大多數計算機上的專用硬件維護,RTC(實時時鐘)電路一般由電池供電,所以系統可以實時跟蹤電源之間的變化。這個「真實世界時間」也會根據你的位置(時區)和季節(夏時制)進行修改,或者表示爲與UTC(也被稱爲格林尼治標準時間或祖魯時間)的抵消。
第二,有相對時間,由time.perf_counter和time.process_time。這種類型的時間與現實世界的時間沒有明確的關係,從某種意義上說,這種關係是系統和實現特定的。它只能用於測量時間間隔,即與兩個瞬間之間的時間成正比的單位數。這主要用於評估相對性能(例如,此版本的代碼運行速度是否比代碼的版本更快)。
在現代系統中,它是用CPU計數器測量的,它在與CPU硬件時鐘相關的頻率上單調增長。計數器的分辨率高度依賴於系統的硬件,在大多數狀況下,它的值不能可靠地與現實世界的時間相關,甚至沒法在系統間進行比較。此外,每次啓動或重置CPU時,計數器值都會被重置。
time.perf_counter返回計數器的絕對值。time.process_time是一個值,該值從CPU計數器派生而來,但僅在給定進程在CPU上運行時才更新,能夠細分爲「用戶時間」,即進程自己在CPU上運行的時間,以及「系統時間」,即操做系統內核表明進程在CPU上運行的時間。
//參考https://stackoverflow.com/questions/25785243/understanding-time-perf-counter-and-time-process-time
2.4 使用timeit.Timer類來進行時間計算
timeit.Timer(stmt='pass', setup='pass', timer=<timer function>, globals=None)
;
分開time.perf_counter()
2.4.1 Timer的timeit方法
timeit(number=1000000)
將構造函數中的stmt語句執行number遍
2.4.2 Timer的repeat方法
repeat(repeat=5, number=1000000)
重複計時repeat次
2.4.3 Timer的autorange方法
autorange(callback=None)
自動肯定要調用多少次timeit(),它調用timeit()重複使總時間>=0.2秒,返回最終的循環數(循環數,該循環數所需的時間)。它調用timeit()的次數取自從序列1,2,5,10,20,50...,.直到所用的時間至少是0.2秒。
若是callback不爲空,在每次調用完都會執行callback(number, time_taken)
2.4.4 Timer的print_exc方法
print_exc(file=None)
打印錯誤信息,默認打印到sys.stderr
示例:
2.4.5 Timer綜合使用
程序計算階乘,在初始構造時,傳入的參數stmt爲factorial
是每次都執行的語句,而from test_timeit import factorial;x=n;x += 10;
只在初始化時執行一次,語句間使用了;
做爲分隔,同時還傳入了全局變量n
。以後打印輸出執行1000遍的時間,然後打印重複10次、每次執行10000遍的時間,以後經過構造lambda表達式做爲callback,使用autorange自動執行,直到時間達到0.2s,每次執行到必定的次數都會調用callback,打印number和time_taken。具體代碼以下:
import timeit def factorial(n): if n == 1: return 1 return n * factorial(n-1) if __name__ == "__main__": n = 10 t = timeit.Timer("factorial(x)", "from test_timeit import factorial;x=n;x += 10;", globals={'n': n}) print(t.timeit(1000)) print(t.repeat(10, 10000)) callback = lambda number, time_taken: print("number:%d, time_taken:%f"%(number, time_taken)) t.autorange(callback)
python3.7的timeit使用體驗比python2的timeit好,實際上除了timeit以外的其它不少方面也是,如今的學習之路打算所有轉向python3了。
返回目錄