一文看得 Linux 性能分析
最近線上運行的程序出現(xiàn)性能問題,但通過分析程序源代碼(Code Review),并找不到導致問題的根本原因。所以,只能借助強大的性能分析工具 perf 來找出問題所在。
perf 工具的功能非常強大,但本文并不是介紹 perf 工具的使用,而是介紹 perf 的實現(xiàn)原理。介紹 perf 使用的文章多如牛毛,但介紹 perf 原理和實現(xiàn)的卻鳳毛麟角。
但正因為 perf 功能非常強大,所以其實現(xiàn)也是非常復雜的。本文只介紹其中的一個功能:分析進程中的函數(shù)調(diào)用頻率。
接下來,我們先介紹怎么使用 perf 來分析進程中的函數(shù)調(diào)用頻率。
使用 perf 分析程序性能瓶頸
在介紹 perf 的實現(xiàn)之前,我們先使用 perf 分析一個簡單的程序,此程序代碼如下:
上面的程序很簡單,我們創(chuàng)建兩個函數(shù):workload1? 和 workload2?。從代碼可以看出,workload2? 的負載是 workload1 的2倍。
現(xiàn)在我們使用 perf 來分析這個程序的性能瓶頸在哪里。
首先我們將程序編譯成可執(zhí)行文件,編譯時記得加上-g 參數(shù),這樣 perf 才能獲取到函數(shù)名。
使用 perf 的record 命令來記錄程序的運行情況。
運行上面的命令后,將會生成一個 perf.data 的文件,此文件記錄了 sample 程序運行時的采樣數(shù)據(jù)。
使用 perf 的report 命令分析程序的運行情況。
結(jié)果如下圖所示:
從上圖可以看出,函數(shù) workload2(65%)的負載大概是函數(shù) workload1(35%)的 2 倍,與我們的代碼基本一致。
perf 實現(xiàn)原理
通過上面的例子,我們大概知道怎么使用 perf 來分析程序的性能瓶頸。接下來,我們將會介紹 perf 的內(nèi)部實現(xiàn)原理。
來思考一下,如果讓我們來設計一個統(tǒng)計程序中各個函數(shù)占用 CPU 時間的方案,應該如何設計?最簡單的方案就是:在各個函數(shù)的開始記錄當前時間,然后在函數(shù)執(zhí)行結(jié)束后,使用當前時間減去函數(shù)開始執(zhí)行時的時間,得到函數(shù)的執(zhí)行時間總時長。如下偽代碼:
雖然上述方式可以統(tǒng)計程序中各個函數(shù)的耗時情況,但卻存在很多問題:
- 代碼入侵度高。由于要對每個函數(shù)進行耗時記錄,所以必須在調(diào)用函數(shù)前和調(diào)用函數(shù)后加入統(tǒng)計代碼。
- 統(tǒng)計函數(shù)耗時,并不能反映該函數(shù)的真實 CPU 使用率。比如函數(shù)內(nèi)部調(diào)用了導致進程休眠的系統(tǒng)調(diào)用(如sleep),這時函數(shù)實際上是不使用CPU的,但函數(shù)的耗時卻統(tǒng)計了休眠的時間。
- 對性能影響較大。由于程序中所有函數(shù)都加入統(tǒng)計代碼,所以對性能的影響是非常大的。
所以我們需要一個系統(tǒng),它能夠避免上述問題:
- 零代碼入侵。
- 能夠真實反映函數(shù)的 CPU 使用率。
- 對性能影響較小。
perf 就是為了解決上述問題而生的,我們先來介紹一下 perf 的原理。
采樣
為了減小對程序性能的影響,perf 并不會在每個函數(shù)加入統(tǒng)計代碼,取而代之的統(tǒng)計方式是:采樣。
采樣的原理是:設置一個定時器,當定時器觸發(fā)時,查看當前進程正在執(zhí)行的函數(shù),然后記錄下來。如下圖所示:
如上圖所示,每個 cpu-clock? 是一個定時器的觸發(fā)點。在 6 次定時器觸發(fā)點中,函數(shù) func1? 被命中了 3 次,函數(shù) func2? 被命中了 1 次,函數(shù) func3 被命中了 2 次。所以,我們可以推測出,函數(shù) func1 的 CPU 使用率最高。
排序
如果程序有成千上萬的函數(shù),那么采樣出來的數(shù)據(jù)可能非常多,這個時候就需要對采樣的數(shù)據(jù)進行排序。
為了對采樣數(shù)據(jù)進行排序,perf 使用紅黑樹這種數(shù)據(jù)結(jié)構(gòu),如下圖所示:
如上圖所示,在 perf 采樣的數(shù)據(jù)中,有 7 個函數(shù)被統(tǒng)計了命中次數(shù),perf 使用采樣到的數(shù)據(jù)構(gòu)建一棵紅黑樹。
根據(jù)紅黑樹的特性,最右邊的節(jié)點就是被命中最多的函數(shù),這樣就能把程序中 CPU 使用率最高的函數(shù)找出來。
總結(jié)
由于 perf 的功能非常強大,所以本文也只介紹了 perf 其中一種功能:統(tǒng)計函數(shù)的 CPU 使用率。
在下一篇文章中,我們將會介紹 perf 的代碼實現(xiàn)。Linux 的創(chuàng)始人 Linus 曾經(jīng)說過:Read the f**king source code,要真正理解一個系統(tǒng),只能通過閱讀其源碼。