如何用perf工具


Perf 簡介

Perf 是用來進行軟件性能分析的工具。

通過它,應用程序可以利用 PMU,tracepoint 和內核中的特殊計數器來進行性能統計。它不但可以分析指定應用程序的性能問題 (per thread),也可以用來分析內核的性能問題,當然也可以同時分析應用代碼和內核,從而全面理解應用程序中的性能瓶頸。

最初的時候,它叫做 Performance counter,在 2.6.31 中第一次亮相。此后他成為內核開發最為活躍的一個領域。在 2.6.32 中它正式改名為 Performance Event,因為 perf 已不再僅僅作為 PMU 的抽象,而是能夠處理所有的性能相關的事件。

使用 perf,您可以分析程序運行期間發生的硬件事件,比如 instructions retired ,processor clock cycles 等;您也可以分析軟件事件,比如 Page Fault 和進程切換。

這使得 Perf 擁有了眾多的性能分析能力,舉例來說,使用 Perf 可以計算每個時鍾周期內的指令數,稱為 IPC,IPC 偏低表明代碼沒有很好地利用 CPU。Perf 還可以對程序進行函數級別的采樣,從而了解程序的性能瓶頸究竟在哪里等等。Perf 還可以替代 strace,可以添加動態內核 probe 點,還可以做 benchmark 衡量調度器的好壞

 

 

perf 的基本使用

考查下面這個例子程序。其中函數 longa() 是個很長的循環,比較浪費時間。函數 foo1 和 foo2 將分別調用該函數 10 次,以及 100 次

 1 //test.c 
 2 void longa() 
 3 { 
 4   int i,j; 
 5   for(i = 0; i < 1000000; i++) 
 6   j=i; //am I silly or crazy? I feel boring and desperate. 
 7 } 
 8  
 9 void foo2() 
10 { 
11   int i; 
12   for(i=0 ; i < 10; i++) 
13        longa(); 
14 } 
15  
16 void foo1() 
17 { 
18   int i; 
19   for(i = 0; i< 100; i++) 
20      longa(); 
21 } 
22  
23 int main(void) 
24 { 
25   foo1(); 
26   foo2(); 
27 }

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

 

Perf list,perf 事件

使用 perf list 命令可以列出所有能夠觸發 perf 采樣點的事件。比如

$ perf list 
 List of pre-defined events (to be used in -e): 
 cpu-cycles OR cycles [Hardware event] 
 instructions [Hardware event] 
…
 cpu-clock [Software event] 
 task-clock [Software event] 
 context-switches OR cs [Software event] 
…
 ext4:ext4_allocate_inode [Tracepoint event] 
 kmem:kmalloc [Tracepoint event] 
 module:module_load [Tracepoint event] 
 workqueue:workqueue_execution [Tracepoint event] 
 sched:sched_{wakeup,switch} [Tracepoint event] 
 syscalls:sys_{enter,exit}_epoll_wait [Tracepoint event] 
…

Hardware Event 是由 PMU 硬件產生的事件,比如 cache 命中,當您需要了解程序對硬件特性的使用情況時,便需要對這些事件進行采樣;

Software Event 是內核軟件產生的事件,比如進程切換,tick 數等 ;

Tracepoint event 是內核中的靜態 tracepoint 所觸發的事件,這些 tracepoint 用來判斷程序運行期間內核的行為細節,比如 slab 分配器的分配次數等。

 

Perf stat

有些程序慢是因為計算量太大,其多數時間都應該在使用 CPU 進行計算,這叫做 CPU bound 型;有些程序慢是因為過多的 IO,這種時候其 CPU 利用率應該不高,這叫做 IO bound 型;對於 CPU bound 程序的調優和 IO bound 的調優是不同的。

 1 $perf stat ./t1 
 2  Performance counter stats for './t1': 
 3  
 4  262.738415 task-clock-msecs # 0.991 CPUs 
 5  2 context-switches # 0.000 M/sec 
 6  1 CPU-migrations # 0.000 M/sec 
 7  81 page-faults # 0.000 M/sec 
 8  9478851 cycles # 36.077 M/sec (scaled from 98.24%) 
 9  6771 instructions # 0.001 IPC (scaled from 98.99%) 
10  111114049 branches # 422.908 M/sec (scaled from 99.37%) 
11  8495 branch-misses # 0.008 % (scaled from 95.91%) 
12  12152161 cache-references # 46.252 M/sec (scaled from 96.16%) 
13  7245338 cache-misses # 27.576 M/sec (scaled from 95.49%) 
14  
15   0.265238069 seconds time elapsed 
16  
17 上面告訴我們,程序 t1 是一個 CPU bound 型,因為 task-clock-msecs 接近 1

對 t1 進行調優應該要找到熱點 ( 即最耗時的代碼片段 ),再看看是否能夠提高熱點代碼的效率。

缺省情況下,除了 task-clock-msecs 之外,perf stat 還給出了其他幾個最常用的統計信息:

Task-clock-msecs:CPU 利用率,該值高,說明程序的多數時間花費在 CPU 計算上而非 IO。

Context-switches:進程切換次數,記錄了程序運行過程中發生了多少次進程切換,頻繁的進程切換是應該避免的。

Cache-misses:程序運行過程中總體的 cache 利用情況,如果該值過高,說明程序的 cache 利用不好

CPU-migrations:表示進程 t1 運行過程中發生了多少次 CPU 遷移,即被調度器從一個 CPU 轉移到另外一個 CPU 上運行。

Cycles:處理器時鍾,一條機器指令可能需要多個 cycles,

Instructions: 機器指令數目。

IPC:是 Instructions/Cycles 的比值,該值越大越好,說明程序充分利用了處理器的特性。

Cache-references: cache 命中的次數

Cache-misses: cache 失效的次數。

通過指定 -e 選項,您可以改變 perf stat 的缺省事件 ( 關於事件,在上一小節已經說明,可以通過 perf list 來查看 )。假如您已經有很多的調優經驗,可能會使用 -e 選項來查看您所感興趣的特殊的事件

 

Perf record  解讀 report

使用 top 和 stat 之后,您可能已經大致有數了。要進一步分析,便需要一些粒度更細的信息。比如說您已經斷定目標程序計算量較大,也許是因為有些代碼寫的不夠精簡。那么面對長長的代碼文件,究竟哪幾行代碼需要進一步修改呢?這便需要使用 perf record 記錄單個函數級別的統計信息,並使用 perf report 來顯示統計結果。

您的調優應該將注意力集中到百分比高的熱點代碼片段上,假如一段代碼只占用整個程序運行時間的 0.1%,即使您將其優化到僅剩一條機器指令,恐怕也只能將整體的程序性能提高 0.1%。俗話說,好鋼用在刀刃上,不必我多說了。

1 perf record  ./t1 
2 perf report
 1 Samples: 107  of event 'cycles', Event count (approx.): 889407873
 2 Overhead  Command  Shared Object      Symbol
 3   99.26%  t1       t1                 [.] longa
 4    0.61%  t1       [kernel.kallsyms]  [k] nmi
 5    0.03%  t1       [kernel.kallsyms]  [k] __hrtimer_next_event_base
 6    0.03%  t1       [kernel.kallsyms]  [k] __vma_adjust
 7    0.02%  t1       ld-2.30.so         [.] 0x000000000001e378
 8    0.02%  t1       [kernel.kallsyms]  [k] filemap_map_pages
 9    0.02%  t1       [kernel.kallsyms]  [k] do_user_addr_fault
10    0.01%  t1       [kernel.kallsyms]  [k] move_page_tables
11    0.00%  perf     [kernel.kallsyms]  [k] perf_event_comm_output
12    0.00%  perf     [kernel.kallsyms]  [k] perf_pmu_enable.part.0
13    0.00%  perf     [kernel.kallsyms]  [k] sched_clock
14    0.00%  perf     [kernel.kallsyms]  [k] native_write_msr

不出所料,hot spot 是 longa( ) 函數

 

火焰圖

1、Flame Graph項目位於GitHub上:https://github.com/brendangregg/FlameGraph

2、可以用git將其clone下來:git clone https://github.com/brendangregg/FlameGraph.git

 

我們以perf為例,看一下flamegraph的使用方法:

1、第一步

$sudo perf record -e cpu-clock -g -p 28591

Ctrl+c結束執行后,在當前目錄下會生成采樣數據perf.data.

2、第二步 

用perf script工具對perf.data進行解析

perf script -i perf.data &> perf.unfold

3、第三步

將perf.unfold中的符號進行折疊:

#./stackcollapse-perf.pl perf.unfold &> perf.folded

4、最后生成svg圖:

./flamegraph.pl perf.folded > perf.svg

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM