監(jiān)控 Python 內(nèi)存使用情況和代碼執(zhí)行時間!
來源:網(wǎng)絡
我的代碼的哪些部分運行時間最長、內(nèi)存最多?我怎樣才能找到需要改進的地方?”
在開發(fā)過程中,我很確定我們大多數(shù)人都會想知道這一點,在本文中總結(jié)了一些方法來監(jiān)控 Python 代碼的時間和內(nèi)存使用情況。
本文將介紹4種方法,前3種方法提供時間信息,第4個方法可以獲得內(nèi)存使用情況。
-
time 模塊
-
%%time 魔法命令
-
line_profiler
-
memory_profiler
?1. time 模塊
這是計算代碼運行所需時間的最簡單、最直接(但需要手動開發(fā))的方法。他的邏輯也很簡單:記錄代碼運行之前和之后的時間,計算時間之間的差異。這可以實現(xiàn)如下:
?import?time
?start_time?=?time.time()
?result?=?5+2
?end_time?=?time.time()
?print('Time?taken?=?{}?sec'.format(end_time?-?start_time))
下面的例子顯示了for循環(huán)和列表推導式在時間上的差異:
?import?time
?#?for?loop?vs.?list?comp
?list_comp_start_time?=?time.time()
?result?=?[i?for?i?in?range(0,1000000)]
?list_comp_end_time?=?time.time()
?print('Time?taken?for?list?comp?=?{}?sec'.format(list_comp_end_time?-?list_comp_start_time))
?result=[]
?for_loop_start_time?=?time.time()
?for?i?in?range(0,1000000):
?????result.append(i)
?for_loop_end_time?=?time.time()
?print('Time?taken?for?for-loop?=?{}?sec'.format(for_loop_end_time?-?for_loop_start_time))
?list_comp_time?=?list_comp_end_time?-?list_comp_start_time
?for_loop_time?=?for_loop_end_time?-?for_loop_start_time
?print('Difference?=?{}?%'.format((for_loop_time?-?list_comp_time)/list_comp_time?*?100))
我們都知道for會慢一些
?Time?taken?for?list?comp?=?0.05843973159790039?sec
?Time?taken?for?for-loop?=?0.06774497032165527?sec
?Difference?=?15.922795107582594?%
?2. %%time 魔法命令
魔法命令是IPython內(nèi)核中內(nèi)置的方便命令,可以方便地執(zhí)行特定的任務。一般情況下都實在jupyter notebook種使用。
在單元格的開頭添加%%time ,單元格執(zhí)行完成后,會輸出單元格執(zhí)行所花費的時間。
?%%time
?def?convert_cms(cm,?unit='m'):
?????'''
????Function?to?convert?cm?to?m?or?feet
????'''
?????if?unit?==?'m':
?????????return?cm/100
?????return?cm/30.48
?convert_cms(1000)
結(jié)果如下:
?CPU?times:?user?24?μs,?sys:?1?μs,?total:?25?μs
?Wall?time:?28.1?μs
?Out[8]:?10.0
這里的CPU times是CPU處理代碼所花費的實際時間,Wall time是事件經(jīng)過的真實時間,在方法入口和方法出口之間的時間。
?3. line_profiler
前兩個方法只提供執(zhí)行該方法所需的總時間。通過時間分析器我們可以獲得函數(shù)中每一個代碼的運行時間。
這里我們需要使用line_profiler包。使用pip install line_profiler。
?import?line_profiler
?def?convert_cms(cm,?unit='m'):
?????'''
????Function?to?convert?cm?to?m?or?feet
????'''
?????if?unit?==?'m':
?????????return?cm/100
?????return?cm/30.48
?#?Load?the?profiler
?%load_ext?line_profiler
?#?Use?the?profiler's?magic?to?call?the?method
?%lprun?-f?convert_cms?convert_cms(1000,?'f')
輸出結(jié)果如下:
?Timer?unit:?1e-06?s
?Total?time:?4e-06?s
?File:?/var/folders/y_/ff7_m0c146ddrr_mctd4vpkh0000gn/T/ipykernel_22452/382784489.py
?Function:?convert_cms?at?line?1
?Line?#?????Hits?????????Time?Per?Hit???%?Time?Line?Contents
?==============================================================
??????1???????????????????????????????????????????def?convert_cms(cm,?unit='m'):
??????2???????????????????????????????????????????????'''
??????3???????????????????????????????????????????????Function?to?convert?cm?to?m?or?feet
??????4???????????????????????????????????????????????'''
??????5?????????1?????????2.0?????2.0?????50.0?????if?unit?==?'m':
??????6???????????????????????????????????????????????????return?cm/100
??????7?????????1?????????2.0?????2.0?????50.0?????return?cm/30.48
可以看到line_profiler提供了每行代碼所花費時間的詳細信息。
-
Line Contents :運行的代碼
-
Hits:行被執(zhí)行的次數(shù)
-
Time:所花費的總時間(即命中次數(shù)x每次命中次數(shù))
-
Per Hit:一次執(zhí)行花費的時間,也就是說 Time = ?Hits X Per Hit
-
% Time:占總時間的比例
可以看到,每一行代碼都詳細的分析了時間,這對于我們分析時間相當?shù)挠袔椭?/p>
?4. memory_profiler
與line_profiler類似,memory_profiler提供代碼的逐行內(nèi)存使用情況。
要安裝它需要使用pip install memory_profiler。我們這里監(jiān)視convert_cms_f函數(shù)的內(nèi)存使用情況
?from?conversions?import?convert_cms_f
?import?memory_profiler
?%load_ext?memory_profiler
?%mprun?-f?convert_cms_f?convert_cms_f(1000,?'f')
convert_cms_f函數(shù)在單獨的文件中定義,然后導入。結(jié)果如下:
?Line?#???Mem?usage???Increment?Occurrences???Line?Contents
?=============================================================
??????1?????63.7?MiB?????63.7?MiB???????????1???def?convert_cms_f(cm,?unit='m'):
??????2?????????????????????????????????????????????'''
??????3?????????????????????????????????????????????Function?to?convert?cm?to?m?or?feet
??????4?????????????????????????????????????????????'''
??????5?????63.7?MiB?????0.0?MiB???????????1???????if?unit?==?'m':
??????6?????????????????????????????????????????????????return?cm/100
??????7?????63.7?MiB?????0.0?MiB???????????1???????return?cm/30.48memory_profiler 提供對每行代碼內(nèi)存使用情況的詳細了解。
這里的1 MiB (MebiByte) 幾乎等于 1MB。1 MiB ?= 1.048576 1MB
但是memory_profiler 也有一些缺點:它通過查詢操作系統(tǒng)內(nèi)存,所以結(jié)果可能與 python 解釋器略有不同,如果在會話中多次運行 %mprun,可能會注意到增量列報告所有代碼行為 0.0 MiB。這是因為魔法命令的限制導致的。
雖然memory_profiler有一些問題,但是它就使我們能夠清楚地了解內(nèi)存使用情況,對于開發(fā)來說是一個非常好用的工具
?5. 總結(jié)一下
雖然Python并不是一個以執(zhí)行效率見長的語言,但是在某些特殊情況下這些命令對我們還是非常有幫助的。
