profile和line_profiler兩個模塊都是性能分析工具。有時候需要找到代碼中運行速度較慢處或瓶頸,可以通過這兩模塊實現,而不再使用time計時。
line_profiler模塊可以記錄每行代碼的運行時間和耗時百分比。
memory_profiler模塊用來監控進程,記錄每行代碼的內存使用狀況。
profile是python自帶性能分析模塊。line_profiler和memory_profiler模塊都需要自行安裝。
profile
常用方法
profile.run(statement, filename=None, sort=-1)
#filename用來設置輸出文件,默認直接打印
輸出結果的各項含義:
名稱 | 含義 |
---|---|
ncalls | 函數被調用次數 |
tottime | 函數(除去子函數)總運行時間 |
percall | 平均時間,等於tottime/ncalls |
cumtime | 函數(包括子函數)總運行時間 |
percall | 平均時間,等於cumtime/ncalls |
filename | 文件:行號(函數) |
基於上述信息可以分析代碼性能,通過filename可以知道哪個文件第幾行的啥函數運行了多少次,運行時間多少。
示例
import profile
a=[[1,5],[2,3],[6,6],[0,0]]
profile.run('sorted(a,key=lambda x:x[1])')
'''
9 function calls in 0.000 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 :0(exec)
1 0.000 0.000 0.000 0.000 :0(setprofile)
1 0.000 0.000 0.000 0.000 :0(sorted)
4 0.000 0.000 0.000 0.000 <string>:1(<lambda>)
1 0.000 0.000 0.000 0.000 <string>:1(<module>)
0 0.000 0.000 profile:0(profiler)
1 0.000 0.000 0.000 0.000 profile:0(sorted(a,key=lambda x:x[1]))
'''
#通過上述結果,可以看出string中第1行的lambda函數運行了四次
也可以先生成Profiler實例對象,再run:
p=profile.Profile()
p.run('sum') #<profile.Profile at 0x7f01ca552c18>
p.print_stats() #將性能打印出來
line_profiler
常用方法
該模塊常通過裝飾器來使用。在安裝好該模塊後,編寫需要測試的python程序,並在需要測試的函數上方加入@profile
,保存爲*.py文件。在命令行使用kernprof -l -v *.py
,在輸出打印出程序結果之後會打印性能分析。
輸出結果的含義:
名稱 | 意義 |
---|---|
Timer unit | 計時器單位,微秒 |
Total time | 測試代碼總運行時間 |
File | 測試代碼文件名 |
Hits | 每行代碼運行次數 |
Time | 每行代碼運行時間 |
Per Hit | 每行代碼運行一次的時間 |
% Time | 每行代碼運行時間的百分比 |
Line Contents | 每行代碼 |
示例
將下列代碼保存爲test.py,不需要導入line_profiler模塊。
@profile
def compute():
for i in range(100):
print(i**3)
if __name__=='__main__':
compute()
在命令行運行kernprof -l -v test.py
,注意如果test.py不在命令行當前文件夾,需要些寫上路徑。輸出爲:
'''
Wrote profile results to test.py.lprof
Timer unit: 1e-06 s
Total time: 0.012405 s
File: test.py
Function: compute at line 1
Line # Hits Time Per Hit % Time Line Contents
==============================================================
1 @profile
2 def compute():
3 101 74.0 0.7 0.6 for i in range(100):
4 100 12331.0 123.3 99.4 print(i**3)
'''
#可以分析代碼性能,比如 print(i**3)共執行了100次,平均每次運行時間123.3微秒。
memory_profiler
常用方法
使用方法同line_profiler模塊,藉助裝飾器調用。在需要分析的函數上方加@profile
。在命令行使用python -m memory_profiler *.py
運行代碼。
輸出結果中:
Mem usage:內存使用情況
Increment:每行代碼運行後內存增減情況
還有一種調用該模塊的方式,在測試代碼中導入memory_profiler模塊的profile方法,如下:
from memory_profiler import profile
其餘編寫方法同上,裝飾器的參數如下:
@profile(func=None, stream=None, precision=1, backend='psutil')
#stream表示將分析結果輸出的文件,默認爲命令行;precision表示分析結果中數字小數點保留位數。
這種方法在命令行只需運行python *.py
即可,若不需要測試性能,可直接將裝飾器註釋掉。
示例
將下列代碼保存爲test.py,不需要導入line_profiler模塊。
from memory_profiler import profile
@profile(precision=2)
def compute():
a=[]
b=3.4
for i in range(10):
a.append(i)
del b
if __name__=='__main__':
compute()
在命令行運行python test.py
,注意路徑。若代碼中沒有導入memory_profiler模塊的profile方法,則應該運行python -m memory_profiler test.py
。輸出爲:
'''
Line # Mem usage Increment Line Contents
================================================
2 39.08 MiB 39.08 MiB @profile(precision=2)
3 def compute():
4 39.08 MiB 0.00 MiB a=[10]*100
5 39.08 MiB 0.00 MiB b=3.4
6 39.08 MiB 0.00 MiB for i in range(10):
7 39.08 MiB 0.00 MiB a.append(i)
8 39.08 MiB 0.00 MiB del b
'''
#輸出分析結果的數字保留了2位小數