簡介: google-perftools 是一款優秀的 Linux C/C++ 程序的性能剖析及優化工具,它提供了將目標程序運行時所消耗的 CPU 時間片進行剖析和圖形化輸出剖析結果的功能。本文將從零開始,一步一步引導讀者搭建並運行一個 google-perftools 的剖析環境,並用一個示例來演示如何使用該工具找到目標程序的性能瓶頸。
google-perftools 是一款針對 C/C++ 程序的性能分析工具,它是一個遵守 BSD 協議的開源項目。使用該工具可以對 CPU 時間片、內存等系統資源的分配和使用進行分析,本文將重點介紹如何進行 CPU 時間片的剖析。 google-perftools 對一個程序的 CPU 性能剖析包括以下幾個步驟。
1. 編譯目標程序,加入對 google-perftools 庫的依賴。
2. 運行目標程序,並用某種方式啓動 / 終止剖析函數併產生剖析結果。
3. 運行剖結果轉換工具,將不可讀的結果數據轉化成某種格式的文檔(例如 pdf,txt,gv 等)。
您可以在 google-perftools 的網站 (http://code.google.com/p/google-perftools/downloads/list) 上下載最新版的安裝包。爲完成步驟 3 的工作,您還需要一個將剖析結果轉化爲程序員可讀文檔的工具,例如 gv(http://www.gnu.org/software/gv/)。
您需要在原有的編譯選項中加入對 libprofiler.so 的引用,這樣在目標程序運行時會加載工具的動態庫。例如本例中作者的系統中,libprofiler.so 安裝在"/usr/lib"目錄下,所以需要在 makefile 文件中的編譯選項加入“-L/usr/lib -lprofiler”。
google-perftools 需要在目標代碼的開始和結尾點分別調用剖析模塊的啓動和終止函數,這樣在目標程序運行時就可以對這段時間內程序實際佔用的 CPU 時間片進行統計和分析。工具的啓動和終止可以採用以下兩種方式。
a. 使用調試工具 gdb 在程序中手動運行性能工具的啓動 / 終止函數。
gdb 是 Linux 上廣泛使用的調試工具,它提供了強大的命令行功能,使我們可以在程序運行時插入斷點並在斷點處執行其他函數。具體的文檔請參照 http://www.gnu.org/software/gdb/,本文中將只對用到的幾個基本功能進行簡單介紹。使用以下幾個功能就可以滿足我們性能調試的基本需求,具體使用請參見下文示例。
命令 | 功能 |
---|---|
ctrl+c | 暫停程序的運行 |
c | 繼續程序的運行 |
b | 添加函數斷點(參數可以是源代碼中的行號或者一個函數名) |
p | 打印某個量的值或者執行一個函數調用 |
b. 在目標代碼中直接加入性能工具函數的調用,該方法就是在程序代碼中直接加入調試函數的調用。
兩種方式都需要對目標程序重新編譯,加入對性能工具的庫依賴。對於前者,他的好處是使用比較靈活,但工具的啓動和終止依賴於程序員的手動操作,常常需要一些暫停函數(比如休眠 sleep)的支持才能達到控制程序的目的,因此精度可能受到影響。對於後者,它需要對目標代碼的進行修改,需要處理函數聲明等問題,但得到的結果精度較高,缺點是每次重新設置啓動點都需要重新編譯,靈活度不高,讀者可以根據自己的實際需求採用有效的方式。
該程序是一個簡單的例子,文中有兩處耗時的無用操作,並且二者間有一定的調用關係。
清單 1. 示例程序
void consumeSomeCPUTime1(int input){ int i = 0; input++; while(i++ < 10000){ i--; i++; i--; i++; } }; void consumeSomeCPUTime2(int input){ input++; consumeSomeCPUTime1(input); int i = 0; while(i++ < 10000){ i--; i++; i--; i++; } }; int stupidComputing(int a, int b){ int i = 0; while( i++ < 10000){ consumeSomeCPUTime1(i); } int j = 0; while(j++ < 5000){ consumeSomeCPUTime2(j); } return a+b; }; int smartComputing(int a, int b){ return a+b; }; void main(){ int i = 0; printf("reached the start point of performance bottle neck\n"); sleep(5); //ProfilerStart("CPUProfile"); while( i++ < 10){ printf("Stupid computing return : %d\n",stupidComputing(i, i+1)); printf("Smart computing return %d\n",smartComputing(i+1, i+2)); } printf("should teminate profiling now.\n"); sleep(5); //ProfilerStop(); } |
源代碼中粗體的內容(方法 1)和斜體的內容(方法 2)分別代表了上文中提及胡兩種執行剖析的方式。採用方法二時將直接產生結果,採用方法 1 時需要配合 GDB 的命令來實現剖析的執行和結束,可用的方法有兩種,一種是在程序運行時手動暫停函數的執行,另一種是預設斷點,並在斷點處執行剖析函數,兩種方法(方法 a,方法 b)在命令行中的具體操作如下。
方法 a
gdb YOUR_PROGRAM // 啓動 gdb 並選擇你的程序爲 gdb 的啓動目標
(gdb)r // 運行
// 等待你需要的條件滿足,此處示例中打印了字符
(gdb)Ctrl + c // 暫停當前函數
(gdb)p ProfilerStart("MyProfile")
(gdb)c // 繼續程序運行
// 等待程序打印目標模塊結束,此處示例打印了提示
(gdb)Ctrl + c // 暫停當前函數
(gdb)p ProfilerStop()
方法 b
gdb YOUR_PROGRAM // 啓動 gdb 並選擇你的程序爲 gdb 的啓動目標
(gdb)b main1.c:47 // 對應於耗時模塊的起始點
(gdb)b main1.c:52 // 對應於耗時模塊的終止點
(gdb)r // 運行
(gdb)p ProfilerStart("MyProfile")
(gdb)c // 繼續程序運行
(gdb)p ProfilerStop()
程序執行完畢會在程序的當前工作目錄下產生名爲 MyProfile 的結果文件。我們可以用以下命令產生可視化的結果文檔。
pprof --gv ./codeTest MyProfile
其中 codeTest 對應於用於測試的目標程序文件名,如果您安裝了 pdf 相關的軟件您還可以嘗試生成 pdf 格式的結果文檔,其對應的命令爲
pprof --pdf ./codeTest MyProfile > MyProfile.pdf
轉換後產生的結果文檔如下圖。圖中的數字和框體的大小代表了的某個函數的運行時間佔整個剖析時間的比例。由代碼的邏輯可知,stupidComputing,stupidComputing2 都是費時操作並且它們和 consumeSomeCPUTime 存在着一定的調用關係。
圖 1. 剖析結果
本文介紹了一個 Linux 平臺上的性能剖析工具 google-perftools,並結合實例向讀者展示瞭如何使用該工具配置、使用及分析性能瓶頸。
本文轉自:http://www.ibm.com/developerworks/cn/linux/l-cn-googleperf/index.html