效能工具perf的用法以及如何繪製效能火焰圖

FreeeLinux發表於2018-11-19

perf簡介

Perf是內建於Linux核心原始碼樹中的效能剖析(profiling)工具。其基於事件取樣原理,以效能事件為基礎,常用於效能瓶頸的查詢與熱點程式碼的定位。

效能調優工具如 perf,Oprofile 等的基本原理都是對被監測物件進行取樣,最簡單的情形是根據 tick 中斷進行取樣,即在 tick 中斷內觸發取樣點,在取樣點裡判斷程式當時的上下文。假如一個程式 90% 的時間都花費在函式 foo() 上,那麼 90% 的取樣點都應該落在函式 foo的上下文中。只要取樣頻率足夠高,取樣時間足夠長,那麼以上推論就比較可靠。因此,通過 tick 觸發取樣,我們便可以瞭解程式中哪些地方最耗時間,從而重點分析。

稍微擴充套件一下思路,就可以發現改變取樣的觸發條件使得我們可以獲得不同的統計資料:

  • 以時間點 ( 如 tick) 作為事件觸發取樣便可以獲知程式執行時間的分佈。
  • 以 cache miss 事件觸發取樣便可以知道 cache miss 的分佈,即 cache 失效經常發生在哪些程式程式碼中
  • 等等其他事件

當然,perf使用更多是CPU的PMU計數器,PMU計數器是大部分CPU都有的功能,它們可以用來統計比如L1 Cache失效的次數,分支預測失敗的次數等。PMU可以在這些計數器的計數超過一個特定的值的時候產生一箇中斷,這個中斷,我們可以用和時鐘一樣的方法,來抽樣判斷系統中哪個函式發生了最多的Cache失效,分支預測失效等。

perf 用法

本文示例程式碼:

//
// Created by wilcohuang on 2018/11/19.
//

#include <unistd.h>

using namespace std;

#define NUM 500000

void init(int *int_array) {
    for (int i = 0; i < NUM; i++) {
        int_array[i] = i;
    }
}

void accu(int *int_array, long &sum) {
    for (int i = 0; i < NUM; i++) {
        sum += int_array[i];
        usleep(3);
    }
}

int main() {
    int int_array[NUM];
    init(int_array);
    long sum = 0;
    accu(int_array, sum);
}

說明

perf的使用可以分為兩種方式:

  1. 直接使用perf啟動服務
  2. 掛接到已啟動的程式
    第一種方式不需要root許可權,第二種方式需要root許可權

perf top

用於檢視cpu的主要效能消耗點

跟蹤一個名為main的程式:

perf top -e cycles -p `pgrep main`

輸入如下:
在這裡插入圖片描述

perf record

同樣是分析診斷程式:

perf record -e cpu-clock -g ./run
或者
perf record -e cpu-clock -g -p 4522

使用ctrl+c中斷perf程式,或者在程式執行結束後,會產生perf.data的檔案,使用
perf report
會產生結果分析,如圖
在這裡插入圖片描述

火焰圖

上面通過檔案檢視不夠直觀,還有一種火焰圖分析的方式:
工具下載:
git clone https://github.com/brendangregg/FlameGraph.git
使用命令:

使用perf script工具對perf.data進行解析perf script -i perf.data &> perf.unfold
將perf.unfold中的符號進行摺疊:/data/stackcollapse-perf.pl perf.unfold &> perf.folded
最後生成svg圖:/data/flamegraph.pl perf.folded > perf.svg

然後可以通過chrome或者看圖軟體開啟:
r

Y軸表示呼叫棧,X軸越寬,就表示它被抽到的次數多,即執行的時間長。注意,x 軸不代表時間,而是所有的呼叫棧合併後,按字母順序排列的。

所以,一般我們只需要看有沒有出現 “平頂”,如果有,那麼這個函式可能有效能問題。

perf diff

優化程式效能後,我們自然要看下效果:
perf diff perf.data perf.data.before

我們幹掉了上文中的usleep(3);這句程式碼,然後diff結果輸出如下:

init、accu函式的時間佔比已經將為了0 ?

許可權問題

perf如果不是root,你可以做什麼取決於sysctl設定。kernel.perf_event_paranoid

cat /proc/sys/kernel/perf_event_paranoid

kernel.perf_event_paranoid= 2:您無法進行任何測量。該perf實用程式可能仍然是有用的分析現有的記錄用perf ls,perf report,perf timechart或perf trace。
kernel.perf_event_paranoid= 1:您可以使用perf stator 跟蹤命令perf record,並獲取核心分析資料。
kernel.perf_event_paranoid= 0:您可以使用perf stat或跟蹤命令perf record,並獲取CPU事件資料。
kernel.perf_event_paranoid= -1:您獲得了對核心跟蹤點的原始訪問許可權(具體來說,您可以mmap建立檔案perf_event_open,我不知道其含義是什麼)。

參考

更全的perf參考:

本文參考:

相關文章