Python 效能剖分工具
眼看著項目即將完成,卻被測試人員告知沒有通過效能測試,這種情況在開發中屢見不鮮。接下來的工作就是加班加點地找出效能瓶頸,然後進行最佳化,再進行效能測試,如此這般周而復始直到通過效能測試。儘管豐富的工作經驗有助於效能最佳化,但只有科學地應用工具才能在最短的時間內找出最佳最佳化粒度的瓶頸程式碼片段,達到事半功倍的效果。
profile、cProfile與hotshot
Python 內建了豐富的效能最佳化工具來協助我們定位效能瓶頸,如:profile、cProfile和 hotshot。它們便於使用,而且有完備的支援文檔可供參考。下面以最常用的 profile 模組為例來說明它們的使用方法,假定要剖分的指令檔為 foo.py ,它的內容如下:
def foo():
sum = 0
for i in range(100):
sum += i
return sum
if __name__ == "__main__":
foo()
對 foo.py 進行效能剖分的方法之一是修改 foo.py 裡的 if 程式塊,引入 profile 模組:
if __name__ == "__main__":
import profile
profile.run("foo()")
然後執行 foo.py 即可完成效能剖分,剖分結果將以文本報表的形式列印到標準輸出。
因為上述方法需要修改 foo.py 檔案,所以我們通常更傾向於使用無需修改源檔案的方法——就是在命令列中用應用 python 的 –m 參數來執行 profile :
python –m profile foo.py
除了可以使用 profile 模組外,還可以使用 cProfile 模組。cProfile由 C 語言實現,是剖分代價更低的剖分器,有和 profile 模組相同的介面,但只能用於2.5或以上版本。Python 另一個內建的剖分器是 hotshot,但是 hotshot 模組已經不再推薦使用,因為將來它可能會被移出標準庫。
pstats
無論使用哪個剖分器,它的剖分資料都可以儲存到二進位檔案,如foo.prof。分析和查看剖分結果檔案需要使用 pstats 模組,它極具伸縮性,可以輸出形式多樣的文本報表,是文本介面下不可或缺的工具。
使用 pstats 分析剖分結果很簡單,幾行代碼就可以了:
import pstats
p = pstats.Stats("foo.prof")
p.sort_stats("time").print_stats()
運行上述指令碼將輸出結果為按函數內部已耗用時間(不計調用子函數的時間)長短排序的報表。
sort_stats() 方法是 pstats.Stats 最重要的方法之一,它用以對剖分資料進行排序。sort_stats() 接受一個字串參數,這個字串標識了排序的欄位,常用的可選的參數及其意義如下:
‘ncalls’ |
被調用次數 |
‘cumulative’ |
函數啟動並執行總時間 |
‘nfl’ |
Name/file/line |
‘time’ |
函數內部已耗用時間(不計調用子函數的時間) |
除了 sort_stats() 外, pstats.Stats 還有 print_callees() 和 print_callers() 方法用以輸出指定函數所調用的函數和調用過指定函數的函數。
除了編編程介面外,pstats 還提供了友好的命令列互動環境,在命令列執行 python –m pstats 就可以進入互動環境,在互動環境裡可以使用 read/add 指令讀入/載入剖分結果檔案,stats 指令用以查看報表, callees 和 callers 指令用以查看特定函數的被調用者和調用者。是 pstats 的,標識了它的基本使用方法: