標籤:
效能分析工具
軟體的效能是軟體品質的重要考察點,不論是線上服務程式還是離線程式,甚至是終端應用,效能都是使用者體驗的關鍵。這裡說的效能重大的範疇來講包括了效能和穩定性兩個方面,我們在做軟體測試的時候也是要重點測試版本的效能表現和穩定性的。對於軟體測試過程中發現的效能問題,如何定位有很多的方法。基本的方法可能是開發人員對代碼進行review,或者是使用一些工具對代碼進行效能分析。常見的效能分析tuning工具有哪些呢?下面兩篇文章做了詳細的總結:
- https://computing.llnl.gov/tutorials/performance_tools/#Considerations
- http://en.wikipedia.org/wiki/List_of_performance_analysis_tools
在我工作中主要是關於Linux C++程式碼的效能分析,gprof是可用於Linux C++代碼效能profiling的工具之一,本文主要講講我對gprof的學習和使用過程。
Gprof的基本原理
gprof能夠讓你知道你的代碼哪些地方是比較耗時的,哪些函數是被調用次數很多的,並且能夠讓你一目瞭然的看到函數與函數之間的調用關係。gprof是gcc/g++編譯器支援的一種效能診斷工具。只要在編譯時間加上-pg選項,編譯器就會在編譯器時在每個函數的開頭加一個mcount函數調用,在每一個函數調用之前都會先調用這個mcount函數,在mcount中會儲存函數的呼叫歷程圖和函數的調用時間和被調次數等資訊。最終在程式退出時儲存在gmon.out檔案中,需要注意的是程式必須是正常退出或者通過exit調用退出,因為只要在exit()被調用時才會觸發程式寫gmon.out檔案。
那麼,gprof的使用方法主要以下三步:
- 會用-pg參數編譯器
- 運行程式,並正常退出
- 查看gmon.out檔案
Gprof使用執行個體
- #include<iostream>
- using namespace std;
-
- int add(int a, int b)
- {
- return a+b;
- }
-
- int sub(int a, int b)
- {
- return a-b;
- }
-
- int call ()
- {
- std::cout << add(1,2) << std::endl;
- std::cout << sub(2,4) << std::endl;
- }
-
- int main()
- {
- int a=1, b=2;
- cout << add(a,b) << endl;
- for (int i=0; i<10000; i++)
- call();
- return 0;
- }
使用g++編譯並加上-pg參數:
- g++ -o hello hello_grof.cpp -pg -g
得到可執行檔,我們可以使用readelf查看一下它的符號表裡和沒有-pg時編譯的有啥不同:readelf -r ./hello和readelf -r ./hello_normal得出的結果對比。
左邊為有-pg參數編譯的結果。可以看出多了三個函數符號_mcount, __monstartup, _mcleanup都是和gprof相關的調用。
使用gdb調試hello程式,在mcount函數中打斷點也可以看到其調用關係,在add函數執行前先調用mcount函數:
接下來運行程式./hello,會在目前的目錄下產生gmon.out檔案。使用gprof查看檔案資訊:
- gprof -b ./hello gmon.out
得到如下輸出:
- Flat profile:
-
- Each sample counts as 0.01 seconds.
- no time accumulated
-
- % cumulative self self total
- time seconds seconds calls Ts/call Ts/call name
- 0.00 0.00 0.00 10001 0.00 0.00 add(int, int)
- 0.00 0.00 0.00 10000 0.00 0.00 sub(int, int)
- 0.00 0.00 0.00 10000 0.00 0.00 call()
- 0.00 0.00 0.00 1 0.00 0.00 global constructors keyed to _Z3addii
- 0.00 0.00 0.00 1 0.00 0.00 __static_initialization_and_destruction_0(int, int)
-
- Call graph
-
-
- granularity: each sample hit covers 2 byte(s) no time propagated
-
- index % time self children called name
- 0.00 0.00 1/10001 main [7]
- 0.00 0.00 10000/10001 call() [10]
- [8] 0.0 0.00 0.00 10001 add(int, int) [8]
- -----------------------------------------------
- 0.00 0.00 10000/10000 call() [10]
- [9] 0.0 0.00 0.00 10000 sub(int, int) [9]
- -----------------------------------------------
- 0.00 0.00 10000/10000 main [7]
- [10] 0.0 0.00 0.00 10000 call() [10]
- 0.00 0.00 10000/10001 add(int, int) [8]
- 0.00 0.00 10000/10000 sub(int, int) [9]
- -----------------------------------------------
- 0.00 0.00 1/1 __do_global_ctors_aux [13]
- [11] 0.0 0.00 0.00 1 global constructors keyed to _Z3addii [11]
- 0.00 0.00 1/1 __static_initialization_and_destruction_0(int, int) [12]
- -----------------------------------------------
- 0.00 0.00 1/1 global constructors keyed to _Z3addii [11]
- [12] 0.0 0.00 0.00 1 __static_initialization_and_destruction_0(int, int) [12]
- -----------------------------------------------
-
- Index by function name
-
- [11] global constructors keyed to _Z3addii (hello_grof.cpp) [9] sub(int, int) [10] call()
- [8] add(int, int) [12] __static_initialization_and_destruction_0(int, int) (hello_grof.cpp)
可以使用運行命令:
- gprof -b ./hello gmon.out | gprof2doc.py > ~WWW/hello.dot
產生dot格式的呼叫歷程圖檔案,可以使用windows版的GVEdit for Graphviz軟體查看呼叫歷程圖:
附上一張比較複雜的程式呼叫歷程圖:
對於調用的關係和調用熱點一目瞭然。
Gprof輸出解讀
這部分內容可將gprof -b ./hello中的-b參數去掉,可以顯示欄位的詳細含義描述:
- 14 % the percentage of the total running time of the
- 15 time program used by this function.
- 16
- 17 cumulative a running sum of the number of seconds accounted
- 18 seconds for by this function and those listed above it.
- 19
- 20 self the number of seconds accounted for by this
- 21 seconds function alone. This is the major sort for this
- 22 listing.
- 23
- 24 calls the number of times this function was invoked, if
- 25 this function is profiled, else blank.
- 26
- 27 self the average number of milliseconds spent in this
- 28 ms/call function per call, if this function is profiled,
- 29 else blank.
- 30
- 31 total the average number of milliseconds spent in this
- 32 ms/call function and its descendents per call, if this
- 33 function is profiled, else blank.
- 34
- 35 name the name of the function. This is the minor sort
- 36 for this listing. The index shows the location of
- 37 the function in the gprof listing. If the index is
- 38 in parenthesis it shows where it would appear in
- 39 the gprof listing if it were to be printed.
總結
gprof是常見的效能分析工具,在此羅列一下它的一些不足,也是從網上看的:
- 1、對多線程支援不好,不準確
- 2、必須退出exit()才行
- 3、它只能分析應用程式在運行過程中所消耗掉的使用者時間,無法得到程式核心空間的已耗用時間。對核心態的調用分析無能為力。如果程式系統調用比率比較大,就不適合。
Linux C++程式進行效能分析工具gprof使用入門