使用火焰圖分析CPU性能回退問題
所以,下面就隆重介紹紅/藍差分火焰圖(red/blue differential flame graphs):
上面是一副互動式SVG格式圖片(鏈接)。圖中使用了兩種顏色來表示狀態,紅色表示增長,藍色表示衰減。
這張火焰圖中各火焰的形狀和大小都是和第二次抓取的profile文件對應的CPU火焰圖是相同的。(其中,y軸表示棧的深度,x軸表示樣本的總數,棧幀的寬度表示了profile文件中該函數出現的比例,最頂層表示正在運行的函數,再往下就是調用它的棧)
在下面這個案例展示了,在系統升級後,一個工作載荷的CPU使用率上升了。 下面是對應的CPU火焰圖(SVG格式)
通常,在標準的火焰圖中棧幀和棧塔的顏色是隨機選擇的。 而在紅/藍差分火焰圖中,使用不同的顏色來表示兩個profile文件中的差異部分。
在第二個profile中deflate_slow()函數以及它後續調用的函數運行的次數要比前一次更多,所以在上圖中這個棧幀被標為了紅色。可以看出問題的原因是ZFS的壓縮功能被啟用了,而在系統升級前這項功能是關閉的。
這個例子過於簡單,我甚至可以不用差分火焰圖也能分析出來。但想像一下,如果是在分析一個微小的性能下降,比如說小於5%,而且代碼也更加複雜的時候,問題就為那麼好處理了。
紅/藍差分火焰圖
這個事情我已經討論了好幾年了,最終我自己編寫了一個我個人認為有價值的實現。它的工作原理是這樣的:
- 抓取修改前的堆棧profile1文件
- 抓取修改後的堆棧profile2文件
- 使用profile2來生成火焰圖。(這樣棧幀的寬度就是以profile2文件為基準的)
- 使用「2 - 1」的差異來對火焰圖重新上色。上色的原則是,如果棧幀在profile2中出現出現的次數更多,則標為紅色,否則標為藍色。色彩是根據修改前後的差異來填充的。
這樣做的目的是,同時使用了修改前後的profile文件進行對比,在進行功能驗證測試或者評估代碼修改對性能的影響時,會非常有用。新的火焰圖是基於修改後的profile文件生成(所以棧幀的寬度仍然顯示了當前的CPU消耗),通過顏色的對比,就可以了解到系統性能差異的原因。
只有對性能產生直接影響的函數才會標註顏色(比如說,正在運行的函數),它所調用的子函數不會重複標註。
生成紅/藍差分火焰圖
我已經把一個簡單的代碼實現推送到github上(見火焰圖),其中新增了一個程序腳本,difffolded.pl。為了展示工具是如何工作的,用Linux perf_events 來演示一下操作步驟。(你也可以使用其他profiler)
抓取修改前的profile 1文件:
# perf record -F 99 -a -g -- sleep 30
# perf script > out.stacks1
一段時間後 (或者程序代碼修改後), 抓取profile 2文件:
# perf record -F 99 -a -g -- sleep 30
# perf script > out.stacks2
現在將 profile 文件進行摺疊(fold), 再生成差分火焰圖:
$ git clone --depth 1 http://github.com/brendangregg/FlameGraph
$ cd FlameGraph
$ ./stackcollapse-perf.pl ../out.stacks1 > out.folded1
$ ./stackcollapse-perf.pl ../out.stacks2 > out.folded2
$ ./difffolded.pl out.folded1 out.folded2 | ./flamegraph.pl > diff2.svg
difffolded.p只能對「摺疊」過的堆棧profile文件進行操作,摺疊操作是由前面的stackcollapse系列腳本完成的。(見鏈接火焰圖)。 腳本共輸出3列數據,其中一列代表摺疊的調用棧,另兩列為修改前後profile文件的統計數據。
func_a;func_b;func_c 31 33
[...]
在上面的例子中"funca()->funcb()->func_c()" 代表調用棧,這個調用棧在profile1文件中共出現了31次,在profile2文件中共出現了33次。然後,使用flamegraph.pl腳本處理這3列數據,會自動生成一張紅/藍差分火焰圖。
其他選項
再介紹一些有用的選項:
difffolded.pl -n:這個選項會把兩個profile文件中的數據規範化,使其能相互匹配上。如果你不這樣做,抓取到所有棧的統計值肯定會不相同,因為抓取的時間和CPU負載都不同。這樣的話,看上去要麼就是一片紅(負載增加),要麼就是一片藍(負載下降)。-n選項對第一個profile文件進行了平衡,這樣你就可以得到完整紅/藍圖譜。
difffolded.pl -x: 這個選項會把16進位的地址刪掉。 profiler時常會無法將地址轉換為符號,這樣的話棧里就會有16進位地址。如果這個地址在兩個profile文件中不同,這兩個棧就會認為是不同的棧,而實際上它們是相同的。遇到這樣的問題就用-x選項搞定。
flamegraph.pl --negate: 用於顛倒紅/藍配色。 在下面的章節中,會用到這個功能。
不足之處
雖然我的紅/藍差分火焰圖很有用,但實際上還是有一個問題:如果一個代碼執行路徑完全消失了,那麼在火焰圖中就找不到地方來標註藍色。你只能看到當前的CPU使用情況,而不知道為什麼會變成這樣。
一個辦法是,將對比順序顛倒,畫一個相反的差分火焰圖。例如:
上面的火焰圖是以修改前的profile文件為基準,顏色表達了將要發生的情況。右邊使用藍色高亮顯示的部分,從中可以看出修改後CPU Idle消耗的CPU時間會變少。(其實,我通常會把cpuidle給過濾掉,使用命令行grep -v cpuidle)
圖中把消失的代碼也突顯了出來(或者應該是說,沒有突顯),因為修改前並沒有使能壓縮功能,所以它沒有出現在修改前的profile文件了,也就沒有了被表為紅色的部分。
下面是對應的命令行:
$ ./difffolded.pl out.folded2 out.folded1 | ./flamegraph.pl --negate > diff1.svg
這樣,把前面生成diff2.svg一併使用,我們就能得到:
- diff1.svg: 寬度是以修改前profile文件為基準,顏色表明將要發生的情況
- diff2.svg: 寬度是以修改後profile文件為基準,顏色表明已經發生的情況
如果是在做功能驗證測試,我會同時生成這兩張圖。
CPI 火焰圖
這些腳本開始是被使用在CPI火焰圖的分析上。與比較修改前後的profile文件不同,在分析CPI火焰圖時,可以分析CPU工作周期與停頓周期的差異變化,這樣可以凸顯出CPU的工作狀態來。
其他的差分火焰圖
也有其他人做過類似的工作。Robert Mustacchi在不久前也做了一些嘗試,他使用的方法類似於代碼檢視時的標色風格:只顯示了差異的部分,紅色表示新增(上升)的代碼路徑,藍色表示刪除(下降)的代碼路徑。一個關鍵的差別是棧幀的寬度只體現了差異的樣本數。右邊是一個例子。這個是個很好的主意,但在實際使用中會感覺有點奇怪,因為缺失了完整profile文件的上下文作為背景,這張圖顯得有些難以理解。
Cor-Paul Bezemer也製作了一種差分顯示方法flamegraphdiff,他同時將3張火焰圖放在同一張圖中,修改前後的標準火焰圖各一張,下面再補充了一張差分火焰圖,但棧幀寬度也是差異的樣本數。 上圖是一個例子。在差分圖中將滑鼠移到棧幀上,3張圖中同一棧幀都會被高亮顯示。這種方法中補充了兩張標準的火焰圖,因此解決了上下文的問題。
我們3人的差分火焰圖,都各有所長。三者可以結合起來使用:Cor-Paul方法中上方的兩張圖,可以用我的diff1.svg 和 diff2.svg。下方的火焰圖可以用Robert的方式。為保持一致性,下方的火焰圖可以用我的著色方式:藍->白->紅。
火焰圖正在廣泛傳播中,現在很多公司都在使用它。如果大家知道有其他的實現差分火焰圖的方式,我也不會感到驚訝。(請在評論中告訴我)
結論
如果你遇到了性能回退問題,紅/藍差分火焰圖是找到根因的最快方式。這種方式抓取了兩張普通的火焰圖,然後進行對比,並對差異部分進行標色:紅色表示上升,藍色表示下降。 差分火焰圖是以當前(「修改後」)的profile文件作為基準,形狀和大小都保持不變。因此你通過色彩的差異就能夠很直觀的找到差異部分,且可以看出為什麼會有這樣的差異。
差分火焰圖可以應用到項目的每日構建中,這樣性能回退的問題就可以及時地被發現和修正。
via: http://www.brendangregg.com/blog/2014-11-09/differential-flame-graphs.html
作者:Brendan Gregg 譯者:coloka 校對:wxy
本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive