by Arnout Engelen
<arnouten(Q)bzzt.net>
關於作者:
Arnout Engelen 是荷蘭Nijmegen大學計算機系的學生,也是網絡安全公司TUNIX的雇員。在空閒時間,他喜歡長跑和吹奏高音薩克斯。
摘要:
在優化程序的時候,要記住:在值得優化的地方優化!沒有必要花上幾個小時來優化一段實際上只運行0.04秒的程序。
GProf 使用了一種異常簡單但是非常有效的方法來優化C/C++ 程序,而且能很容易的識別出值得優化的代碼。一個簡單的案例分析將會顯示,GProf如何通過識別並優化兩個關鍵的數據結構,將實際應用中的程序從3分鐘的運行時優化到5秒的。
這個程序最早可以追溯到1982年關於編譯器構建的特別討論大會(the SIGPLAN Symposium on Compiler Construction)。現在這個程序成了各種UNIX 平台上的一個標准工具。
event2dot
,一個將路徑“事件”描述文件轉化為圖形化“dot”文件的工具(executable which translates a pathalizer 'events' file to a graphviz 'dot' file)。簡單的說,它從一個文件裡面讀取各種事件,然後將它們分別保存為圖像(以頁為節點,且將頁與頁之間的轉變作為邊),然後將這些圖像整合為一張大的圖形,並保存為圖形化的'dot'格式文件。
event2dot
並用源碼裡的例子作為輸入(大概55000的數據),大致要三分多鐘:real 3m36.316s
user 0m55.590s
sys 0m1.070s
g++ -pg dotgen.cpp readfile.cpp main.cpp graph.cpp config.cpp -o event2dot
現在我們可以再次運行event2dot
,並使用我們前面使用的測試數據。這次我們運行的時候,event2dot
運行的分析數據會被搜集並保存在'gmon.out'文件中,我們可以通過運行'gprof event2dot
| less'來查看結果。
gprof 會顯示出如下的函數比較重要:
% cumulative self self total可以看出,第一個函數比較重要: 程序裡面絕大部分的運行時都被它給占據了。
time seconds seconds calls s/call s/call name
43.32 46.03 46.03 339952989 0.00 0.00 CompareNodes(Node *,Node *)
25.06 72.66 26.63 55000 0.00 0.00 getNode(char *,NodeListNode *&)
16.80 90.51 17.85 339433374 0.00 0.00 CompareEdges(Edge *,AnnotatedEdge *)
12.70 104.01 13.50 51987 0.00 0.00 addAnnotatedEdge(AnnotatedGraph *,Edge *)
1.98 106.11 2.10 51987 0.00 0.00 addEdge(Graph *,Node *,Node *)
0.07 106.18 0.07 1 0.07 0.07 FindTreshold(AnnotatedEdge *,int)
0.06 106.24 0.06 1 0.06 28.79 getGraphFromFile(char *,NodeListNode *&,Config *)
0.02 106.26 0.02 1 0.02 77.40 summarize(GraphListNode *,Config *)
0.00 106.26 0.00 55000 0.00 0.00 FixName(char *)
CompareNodes
函數上,用 grep 查看一下則發現CompareNodes 只是被CompareEdges
調用了一次而已, 而CompareEdges則只被addAnnotatedEdge
調用——它們都出現在了上面的清單中。這兒就是我們應該做點優化的地方了吧!我們注意到addAnnotatedEdge
遍歷了一個鏈表。雖然鏈表是易於實現,但是卻實在不是最好的數據類型。我們決定將鏈表 g->edges 用二叉樹來代替: 這將會使得查找更快。
real 2m19.314s
user 0m36.370s
sys 0m0.940s
% cumulative self self total看起來以前占用大量運行時的函數現在已經不再是占用運行時的大頭了!我們試一下再優化一下呢:用節點哈希表來取代節點樹。
time seconds seconds calls s/call s/call name
87.01 25.25 25.25 55000 0.00 0.00 getNode(char *,NodeListNode *&)
10.65 28.34 3.09 51987 0.00 0.00 addEdge(Graph *,Node *,Node *)
這次簡直是個巨大的進步:
real 0m3.269s
user 0m0.830s
sys 0m0.090s
perl -d:DProf mycode.pl
來開始,並使用dprofpp
來查看並分析結果。如果你可以用gcj 來編譯你的Java 程序,你也可以使用gprof,然而目前還只支持單線程的Java 代碼。