GProf 是一款經典、輕量的效能剖析工具,自 1982 年問世至今仍是 Linux/Unix 平台 C/C++ 開發者的標準配備。它以極低的側寫開銷,精準定位效能熱點,幫助開發者將執行時間從「幾分鐘」壓縮到「幾秒」。本文以實戰案例 pathalizer 專案中的 event2dot 工具為例,完整示範 GProf 的使用流程與優化策略。
gcc -pg 編譯,編譯器在每個函式進入/離開處插入記錄碼。gmon.out 資料檔,記錄函式呼叫次數、耗時、呼叫關係。gprof executable gmon.out 產生平面剖析(Flat Profile)與呼叫圖,直觀展示熱點函式與呼叫鏈。.dot 格式。real 3m36.316s)。% cumulative self self total
time seconds seconds calls ms/call ms/call name
43.32 46.03 46.03 33995298 0.00 0.00 CompareNodes(Node*, Node*)
25.06 72.66 26.63 550000 0.00 0.00 getNode(char*, NodeListNode*&)
16.80 90.51 17.85 33943337 0.00 0.00 CompareEdges(Edge*, AnnotatedEdge*)
12.70 104.01 13.50 519870 0.00 0.00 addAnnotatedEdge(AnnotatedGraph*, Edge*)
1.98 106.11 2.10 519870 0.00 0.00 addEdge(Graph*, Node*, Node*)
關鍵發現:CompareNodes 佔 43% CPU 時間,被 CompareEdges 呼叫,而 CompareEdges 又被 addAnnotatedEdge 呼叫。這條呼叫鏈是優化突破口。
addAnnotatedEdge 遍歷鏈表查找節點,時間複雜度 O(n)。將鏈表替換為二元搜尋樹,查找降為 O(log n)。
結果:執行時間降至 2 分 19 秒(real 2m19.314s),大幅提升但仍有空間。
% cumulative self self total
time seconds seconds calls ms/call ms/call name
87.01 25.25 25.25 550000 0.00 0.00 getNode(char*, NodeListNode*&)
10.65 28.34 3.09 519870 0.00 0.00 addEdge(Graph*, Node*, Node*)
原本的 CompareNodes 已非瓶頸,getNode(節點查找/建立)成為新熱點(87%)。
將節點儲存結構從二元樹改為雜湊表(Hash Table),查找/插入達到 O(1) 平均複雜度。
驚人結果:執行時間暴跌至 3.269 秒(real 0m3.269s),提速 65 倍!
gprof -q 顯示呼叫關係,追蹤熱點上下文。gprof -A 逐行標註耗時,定位到具體程式行。gprof2dot + Graphviz 產生呼叫圖 PNG;KProf、cgprof 提供 GUI 介面。Devel::DProf 模組 + dprofpp。gcj 編譯為原生碼,可用 GProf(僅支援單執行緒)。cProfile、py-spy 等原生工具更適合。掌握 GProf,讓效能優化從「盲人摸象」變成「手術刀精準切除」,是每位 C/C++ 開發者的必備技能。