六星源課堂:輕松分析 Python 代碼的 9 種軟件庫
編程語言有兩大衡量因素:開發(fā)速度和執(zhí)行速度。Python
?語言關(guān)注開發(fā)速度而非執(zhí)行速度。你有可能會(huì)有疑惑,Python
?代碼總是能很快的完成任務(wù),執(zhí)行速度不快嗎?很多情況下?Python
?相對(duì)會(huì)慢很多,這時(shí)候你就需要找出滯后的位置和原因,并采取對(duì)應(yīng)措施。

軟件開發(fā)和工程領(lǐng)域有一句名言,"Measure, don’t guess",翻譯成中文大致就是不要主觀猜測(cè),而要去測(cè)量。使用軟件過程中如果出現(xiàn)問題,我們不應(yīng)該假設(shè)問題的緣由,最好的方式應(yīng)該是使用相對(duì)應(yīng)的應(yīng)用程序來測(cè)試,找到問題的真正原因。
Python
?也提供了對(duì)應(yīng)的工具,可以分析應(yīng)用程序的性能,這些工具范圍很廣,從標(biāo)準(zhǔn)庫中的簡(jiǎn)單單行程序到從正在運(yùn)行的應(yīng)用程序中收集數(shù)據(jù)的復(fù)雜框架。本文介紹其中最重要的九個(gè),大多都支持跨平臺(tái)運(yùn)行。
Time and Timeit
最簡(jiǎn)單的分析功能是秒表分析,僅分析兩段代碼的執(zhí)行時(shí)間。Python
?帶有兩個(gè)可用作秒表的功能函數(shù)的標(biāo)準(zhǔn)庫。其中,Time
?模塊具備 perf_counter 功能,可以調(diào)用操作系統(tǒng)的高精度定時(shí)器,來獲得時(shí)間戳。其基本原理是:在目標(biāo)操作開始前,調(diào)用一次 time.perf_counter,然后在操作完成時(shí),再調(diào)用一次,以獲得兩次的時(shí)間差。這是一種非常簡(jiǎn)便易行的時(shí)間獲取方式。
而?Timeit
?模塊則是會(huì)對(duì)?Python
?代碼進(jìn)行基準(zhǔn)測(cè)試。Timeit
.Timeit
?功能函數(shù)會(huì)截取一個(gè)代碼段,運(yùn)行多次(默認(rèn)為 1 百萬次)求平均值,來獲得執(zhí)行該操作所需的平均時(shí)間。可以用它來確定某個(gè)緊密的循環(huán)中,單一操作或函數(shù)的調(diào)用時(shí)長(zhǎng)。例如,如果想判斷一個(gè)列表解析式(list comprehension)與一個(gè)常規(guī)列表結(jié)構(gòu),那個(gè)執(zhí)行更快(列表解析式通常更快)。
這兩個(gè)模塊都有明顯的缺點(diǎn),?Time
?它只是一個(gè)秒表,而?Timeit
?的缺點(diǎn)在于:其主要用例是各個(gè)行或代碼塊上的各個(gè)細(xì)微標(biāo)準(zhǔn)差(microbenchmarks)。也就是說,僅當(dāng)這些代碼被單獨(dú)處理時(shí),這種比較才會(huì)有意義。因此,這兩種方法都不足以對(duì)整個(gè)程序進(jìn)行分析。一旦出現(xiàn)數(shù)千行的代碼,這兩種方法都會(huì)耗費(fèi)您大量的時(shí)間。
cProfile
Python
?標(biāo)準(zhǔn)庫還帶有一個(gè)整體程序分析器--cProfile
。當(dāng)程序運(yùn)行時(shí),cProfile
?會(huì)通過跟蹤代碼中的每個(gè)函數(shù)的調(diào)用,以生成一個(gè)包含了最常調(diào)用函數(shù)、以及平均調(diào)用時(shí)間的列表。cProfile
?有三大優(yōu)勢(shì):
包含在標(biāo)準(zhǔn)庫中,現(xiàn)有的?
Python
?安裝包已經(jīng)包含了?cProfile
可以分析相關(guān)調(diào)用行為的多種不同信息。例如:它能夠?qū)⒑瘮?shù)調(diào)用自己的指令所花費(fèi)的時(shí)間,與該函數(shù)所有的其他調(diào)用耗時(shí)區(qū)分開來。據(jù)此,可以判定出到底是該函數(shù)本身運(yùn)行緩慢,還是在其他調(diào)用時(shí)出現(xiàn)的緩慢。
可以自由的約束?
cProfile
。既可以對(duì)整個(gè)程序的運(yùn)行進(jìn)行采樣,又可以僅在指定的函數(shù)運(yùn)行時(shí)啟用概要分析(toggle profiling)。通過縮小范圍和去除分析時(shí)產(chǎn)生的“噪聲”,您可以更好地關(guān)注該函數(shù)的功能與調(diào)用。
cProfile
?也有明顯的缺點(diǎn):
默認(rèn)情況下,它會(huì)設(shè)置多個(gè)采集點(diǎn),生成大量的統(tǒng)計(jì)信息。
根據(jù)其執(zhí)行模型,它在捕獲每個(gè)函數(shù)調(diào)用時(shí),都會(huì)產(chǎn)生大量的開銷。因此?
cProfile
?不適合使用實(shí)時(shí)數(shù)據(jù)的方式,對(duì)生產(chǎn)環(huán)境中的應(yīng)用程序進(jìn)行性能分析,更適合于針對(duì)開發(fā)過程中的性能分析。
FunctionTrace
FunctionTrace
?的工作原理與?cProfile
?類似:無需向代碼添加檢測(cè),只需將分析的腳本的名稱傳遞給它即可,最后它會(huì)生成函數(shù)調(diào)用和內(nèi)存使用情況的詳細(xì)跟蹤報(bào)告。此外,FunctionTrace
?還可以處理多線程/多進(jìn)程應(yīng)用程序。
與?cProfile
?類似,FunctionTrace
?也沒有使用采樣功能,而是記錄每一個(gè)動(dòng)作,其分析組件基于 Rust 編寫,開發(fā)人員表示,其對(duì)應(yīng)用程序施加的開銷小于 10%。FunctionTrace
?有兩大優(yōu)勢(shì):
跟蹤數(shù)據(jù)為 JSON 格式,解析簡(jiǎn)單
可使用?Firefox Profiler?(可以在任何支持 JavaScript 的瀏覽器中運(yùn)行,而不僅僅是 Firefox)將結(jié)果呈現(xiàn)為交互式圖形。

Palanteer
Palanteer
?可用于分析?Python
?和 C++ 程序。Palanteer
?有很多優(yōu)勢(shì):
Palanteer
?還可以在桌面上運(yùn)行的 GUI 應(yīng)用程序中實(shí)時(shí)顯示檢測(cè)結(jié)果。利用?
Palanteer
?來檢測(cè)?Python
?程序非常簡(jiǎn)單,函數(shù)調(diào)用、異常、垃圾收集和操作系統(tǒng)級(jí)別的內(nèi)存分配都可以被跟蹤。如果應(yīng)用程序性能問題與內(nèi)存使用或者對(duì)象分配有關(guān),最后兩項(xiàng)將特別有用。
Palanteer
?也有明顯的缺點(diǎn),如果你想使用它必須完全從源代碼開始構(gòu)建它。
Pyinstrument
Pyinstrument
?與?cProfile
?的工作方式類似,能夠通過跟蹤目標(biāo)程序,以報(bào)告的形式,反映出那些占用了大部分運(yùn)行時(shí)間的代碼。不過,與?cProfile
?相比,Pyinstrument
?的優(yōu)點(diǎn)主要有兩點(diǎn):
Pyinstrument
?不會(huì)去掛鉤(hook)函數(shù)調(diào)用的每個(gè)實(shí)例,而是會(huì)以毫秒為間隔,對(duì)程序的調(diào)用棧進(jìn)行采樣,因此能夠靈敏地檢測(cè)出程序中最耗費(fèi)運(yùn)行時(shí)的部分。Pyinstrument
?的報(bào)告要簡(jiǎn)潔很多。它能夠通過突出顯示程序中占用時(shí)間最多的函數(shù),以便您能盡快地發(fā)現(xiàn)問題,并專注分析原因。Pyinstrument
?同樣具有?cProfile
?的各種優(yōu)點(diǎn),可以將它用作應(yīng)用程序中的對(duì)象,來記錄所選功能,而不是整個(gè)程序的行為。Pyinstrument
?還提供包括 HTML 格式在內(nèi)的多種輸出形式。當(dāng)然,
此外,如下兩個(gè)方面值得您的注意:
某些通過 C 編譯的擴(kuò)展程序(例如使用 Cython 創(chuàng)建的程序),會(huì)在通過命令行進(jìn)行?Pyinstrument
?調(diào)用時(shí),可能無法正常工作。不過,如果您在程序本身使用了?Pyinstrument
,例如:通過使用?Pyinstrument
?分析器的調(diào)用包裝了 main() 函數(shù),那么它們還是能夠正常工作的。Pyinstrument
?不能很好地處理在多個(gè)線程中運(yùn)行的代碼。此時(shí),您可能需要考慮使用下面將要介紹到的?Py-spy
。
Py-spy
Py-spy
?在工作原理與?Pyinstrument
?一樣,都是定期采集程序調(diào)用棧的狀態(tài),而不是記錄每一個(gè)調(diào)用。與?Pyinstrument
?不同的是,Py-spy
?使用 Rust 編寫的核心組件(而?Pyinstrument
?使用 C 擴(kuò)展),運(yùn)行的是帶有分析程序的外進(jìn)程(out-of-process),因此它可以安全地與生產(chǎn)環(huán)境中的代碼協(xié)同使用。
Py-spy
?能夠輕松地完成許多其他分析工具無法實(shí)現(xiàn)的任務(wù),其中包括:分析多線程或帶有子處理(subprocessed)機(jī)制的?Python
?程序等。此外,Py-spy
?也可以分析那些使用符號(hào)進(jìn)行過編譯的 C 擴(kuò)展程序。而對(duì)于使用了 Cython 編譯的擴(kuò)展程序,Py-spy
?需要使用對(duì)應(yīng)生成的 C 文件,以便收集正確的跟蹤信息。
利用?Py-spy
?檢查應(yīng)用程序有如下兩種基本方法,來:
使用?
Py-spy
?的 record 命令,并在運(yùn)行結(jié)束后會(huì)生成火焰圖(flame graph)。使用?
Py-spy
?的 top 命令,通過實(shí)時(shí)更新,交互式地顯示?Python
?應(yīng)用程序的內(nèi)部,并以與 Unix 的 top 工具相同的方式顯示信息。而且那些單線程棧也可以通過命令行被顯示出來。
但是,Py-spy
?有一個(gè)很大的卻帶你:主要適用于從外部分析整個(gè)程序、或是某些組件,不適合對(duì)某個(gè)特定的功能函數(shù)進(jìn)行采樣。
Snakeviz
cProfile
?不支持可視化功能,通常使用?pstats
?來實(shí)現(xiàn)其跟蹤文件的可視化,但是?pstats
?生成的可視化有時(shí)并非是你所需要的。
Snakeviz
?可以利用?cProfile
?生成的數(shù)據(jù)并生成易于閱讀的交互式圖形,最終以 HTML 呈現(xiàn)。目前提供兩種可用的交互式圖形: 每種圖形都可以清晰的顯示各部分所用的時(shí)間。圖表的每一段代表一個(gè)函數(shù)的調(diào)用時(shí)間。單擊一個(gè)段可以檢查它下面的堆棧中的所有內(nèi)容所花費(fèi)的時(shí)間。
Snakeviz
?還生成可搜索和可排序的 HTML 表格視圖;相較于?pstats
?,可創(chuàng)建的跟蹤的更具交互性的版本。

Yappi
Yappi
?是 Yet Another Python Profiler (另一個(gè)?Python
?分析工具)的縮寫。它在功能上,較上述討論過的工具庫只多不少。在默認(rèn)情況下,PyCharm 自帶?Yappi
,因此使用該 IDE 的用戶中已經(jīng)具備對(duì)于?Yappi
?的內(nèi)置訪問權(quán)限。
Yappi
?的使用需要用指令來修飾目標(biāo)代碼,以便針對(duì)分析機(jī)制進(jìn)行調(diào)用,啟動(dòng),停止和生成報(bào)告。Yappi
?允許根據(jù)測(cè)試的實(shí)際需求,在經(jīng)過時(shí)間(wall time)或 CPU 時(shí)間之間進(jìn)行選擇。前者只是一個(gè)秒表;后者則可以通過系統(tǒng)原生 API,記錄下 CPU 實(shí)際執(zhí)行代碼過程中的用時(shí),以便調(diào)整 I/O 的暫停或線程的休眠。通過 CPU 時(shí)間可以更加精確地了解某些操作(例如:數(shù)字代碼的執(zhí)行)的實(shí)際用時(shí)。
Yappi
?提供了?Yappi.get_thread_stats()
?函數(shù),該函數(shù)可以記錄任何一個(gè)線程活動(dòng),檢索出對(duì)應(yīng)的統(tǒng)計(jì)信息,并分別對(duì)其進(jìn)行分析。而且可以對(duì)統(tǒng)計(jì)數(shù)據(jù)進(jìn)行過濾和細(xì)粒度的排序,實(shí)現(xiàn)類似于?cProfile
?中的操作。
此外,Yappi
?還可以分析 greenlet 和 coroutine (協(xié)程)。作為一種分析并發(fā)代碼的強(qiáng)大工具,被廣泛地用來分析異步 metaphor 。
以上就是本次分享的全部?jī)?nèi)容,想學(xué)習(xí)更多Python技巧,歡迎持續(xù)關(guān)注六星源課堂!