什么是perf?
linux性能調優工具,32內核以上自帶的工具,軟件性能分析。在2.6.31及后續版本的Linux內核里,安裝perf非常的容易。
幾乎能夠處理所有與性能相關的事件。perf可以用於查看熱點函數,查看cashe miss的比率,從而幫助開發者來優化程序性能。
什么是性能事件?
指在處理器或者操作系統中發生,可能影響到程序性能的硬件事件或者軟件事情。
主要關注點在哪里?
算法優化(空間復雜度、時間復雜度)、代碼優化(提到執行速度、減少內存占用)
評估程序對硬件資源的使用情況,例如各級cache的訪問次數,各級cache的丟失次數、流水線停頓周期、前端總線訪問次數等。
評估程序對操作系統資源的使用情況,系統調用次數、上下文切換次數、任務遷移次數。
基本原理?
硬件的話采用PMC(performance monitoring unit)CPU的部件,在特定的條件下探測的性能事件是否發生以及發生的次數。軟件性能測試,內置於kernel,分布在各個功能模塊中,統計和操作系統相關性能事件。
性能調優工具如 perf,Oprofile 等的基本原理都是對被監測對象進行采樣,最簡單的情形是根據 tick 中斷進行采樣,即在 tick 中斷內觸發采樣點,在采樣點里判斷程序當時的上下文。假如一個程序 90% 的時間都花費在函數 foo() 上,那么 90% 的采樣點都應該落在函數 foo() 的上下文中。運氣不可捉摸,但我想只要采樣頻率足夠高,采樣時間足夠長,那么以上推論就比較可靠。因此,通過 tick 觸發采樣,我們便可以了解程序中哪些地方最耗時間,從而重點分析。
稍微擴展一下思路,就可以發現改變采樣的觸發條件使得我們可以獲得不同的統計數據:
以時間點 ( 如 tick) 作為事件觸發采樣便可以獲知程序運行時間的分布。
以 cache miss 事件觸發采樣便可以知道 cache miss 的分布,即 cache 失效經常發生在哪些程序代碼中。如此等等。
如何使用高精度的采樣?
如果需要采用高精度的采樣,需要在制定性能事情時,在事件后添加后綴“:p”或者“:pp”
1
2
3
4
|
0
:無精度保證
1
:采樣指令好觸發性能時間的指令偏差為常數(:p)
2
:盡量保證偏差為
0
(:pp)
3
:保證偏差必須為
0
(:ppp)
|
使用perf list(在root權限下運行),可以列出所有的采樣事件
事件分為以下三種:
1)Hardware Event 是由 PMU 硬件產生的事件,比如 cache 命中,當您需要了解程序對硬件特性的使用情況時,便需要對這些事件進行采樣;
2)Software Event 是內核軟件產生的事件,比如進程切換,tick 數等 ;
3)Tracepoint event 是內核中的靜態 tracepoint 所觸發的事件,這些 tracepoint 用來判斷程序運行期間內核的行為細節,比如 slab 分配器的分配次數等。
上述每一個事件都可以用於采樣,並生成一項統計數據,時至今日,尚沒有文檔對每一個 event 的含義進行詳細解釋。
List of pre-defined events (to be used in -e): cpu-cycles OR cycles [Hardware event]處理器周期事件 stalled-cycles-frontend OR idle-cycles-frontend [Hardware event] stalled-cycles-backend OR idle-cycles-backend [Hardware event] instructions [Hardware event] cache-references [Hardware event] cache-misses [Hardware event] branch-instructions OR branches [Hardware event] branch-misses [Hardware event] bus-cycles [Hardware event] cpu-clock [Software event] task-clock [Software event] page-faults OR faults [Software event] minor-faults [Software event] major-faults [Software event] context-switches OR cs [Software event] cpu-migrations OR migrations [Software event] alignment-faults [Software event] emulation-faults [Software event] L1-dcache-loads [Hardware cache event] L1-dcache-load-misses [Hardware cache event] L1-dcache-stores [Hardware cache event] L1-dcache-store-misses [Hardware cache event] L1-dcache-prefetches [Hardware cache event] L1-dcache-prefetch-misses [Hardware cache event] L1-icache-loads [Hardware cache event] L1-icache-load-misses [Hardware cache event] L1-icache-prefetches [Hardware cache event] L1-icache-prefetch-misses [Hardware cache event] LLC-loads [Hardware cache event] LLC-load-misses [Hardware cache event] LLC-stores [Hardware cache event] LLC-store-misses [Hardware cache event] LLC-prefetches [Hardware cache event] LLC-prefetch-misses [Hardware cache event] dTLB-loads [Hardware cache event] dTLB-load-misses [Hardware cache event] dTLB-stores [Hardware cache event] dTLB-store-misses [Hardware cache event] dTLB-prefetches [Hardware cache event] dTLB-prefetch-misses [Hardware cache event] iTLB-loads [Hardware cache event] iTLB-load-misses [Hardware cache event] branch-loads [Hardware cache event] branch-load-misses [Hardware cache event]
perf的安裝?
安裝 perf 非常簡單, 只要內核版本高於2.6.31的, perf已經被內核支持. 首先安裝內核源碼:
apt-get install linux-source
那么在 /usr/src 目錄下就已經下載好了內核源碼, 我們對源碼包進行解壓, 然后進入 tools/perf 目錄然后敲入下面兩個命令即可:
make
make install
可能因為系統原因, 需要提前安裝下面的開發包:
- apt-get install -y binutils-dev
- apt-get install -y libdw-dev
- apt-get install -y python-dev
- apt-get install -y libnewt-dev
提示:
1)可能在編譯的時候,有報錯大概是由於平台問題,數據類型不匹配,導致所有的warning都被當作error對待:出現這問題的原因是-Werror這個gcc編譯選項。只要在makefile中找到包含這個-Werror選項的句子,將-Werror刪除,或是注釋掉就行了
2)安裝完畢,perf可執行程序往往位於當前目錄,可能不在系統的PATH路徑中,此時需要改變環境變量PATH
使用實例
perf COMMAND [-e event ...] PROGRAM, perf 是采用的這么一個命令格式,
COMMAND一般常用的就是 top, stat, record, report等. 然后用 -e 參數來統計需要關注的事件. 多個事件就用多個 -e 連接.
命令1:perf stat分析程序的整體性能
利用10個典型事件剖析了應用程序。
-
task-clock:目標任務真真占用處理器的時間,單位是毫秒,我們稱之為任務執行時間,后面是任務的處理器占用率(執行時間和持續時間的比值)。持續時間值從任務提交到任務結束的總時間(總時間在stat結束之后會打印出來)。CPU 利用率,該值高,說明程序的多數時間花費在 CPU 計算上而非 IO。
-
context-switches:上下文切換次數,前半部分是切換次數,后面是平均每秒發生次數(M是10的6次方)。
-
cpu-migrations:處理器遷移,linux為了位置各個處理器的負載均衡,會在特定的條件下將某個任務從一個處理器遷往另外一個處理器,此時便是發生了一次處理器遷移。即被調度器從一個 CPU 轉移到另外一個 CPU 上運行。
-
page-fault:缺頁異常,linux內存管理子系統采用了分頁機制,
當應用程序請求的頁面尚未建立、請求的頁面不在內存中或者請求的頁面雖在在內存中,
但是尚未建立物理地址和虛擬地址的映射關系是,會觸發一次缺頁異常。
-
cycles:任務消耗的處理器周期數;處理器時鍾,一條機器指令可能需要多個 cycles;
-
instructions:任務執行期間產生的處理器指令數,IPC(instructions perf cycle)
IPC(Instructions/Cycles )是評價處理器與應用程序性能的重要指標。(很多指令需要多個處理周期才能執行完畢),
IPC越大越好,說明程序充分利用了處理器的特征。
-
branches:程序在執行期間遇到的分支指令數。
-
branch-misses:預測錯誤的分支指令數
-
cache-misses:cache時效的次數
-
cache-references:cache的命中次數
常用的參數如下
1
2
3
4
5
|
-e,指定性能事件
-p,指定分析進程的PID
-t,指定待分析線程的TID
-r N,連續分析N次
-d,全面性能分析,采用更多的性能事件
|
一次分析后的結果如下:
Performance counter stats for process id '21787': 42677.253367 task-clock # 0.142 CPUs utilized 587,906 context-switches # 0.014 M/sec 29,209 CPU-migrations # 0.001 M/sec 117 page-faults # 0.000 M/sec 82,341,400,508 cycles # 1.929 GHz [83.48%] 61,262,984,952 stalled-cycles-frontend # 74.40% frontend cycles idle [83.28%] 43,113,701,768 stalled-cycles-backend # 52.36% backend cycles idle [66.72%] 44,023,301,495 instructions # 0.53 insns per cycle # 1.39 stalled cycles per insn [83.50%] 8,137,448,528 branches # 190.674 M/sec [83.22%] 430,957,756 branch-misses # 5.30% of all branches [83.34%] 300.393753095 seconds time elapsed
考查下面這個例子程序。其中函數 longa() 是個很長的循環,比較浪費時間。函數 foo1 和 foo2 將分別調用該函數 10 次,以及 100 次。
//t1.c void longa() { int i,j; for(i = 0; i < 1000000; i++) j=i; //am I silly or crazy? I feel boring and desperate. } void foo2() { int i; for(i=0 ; i < 10; i++) longa(); } void foo1() { int i; for(i = 0; i< 100; i++) longa(); } int main(void) { foo1(); foo2(); }
然后編譯它: gcc -o t1 -g t1.c
下面演示了 perf stat 針對程序 t1 的輸出:
root@ubuntu-test:~# perf stat ./t1 Performance counter stats for './t1': 218.584169 task-clock # 0.997 CPUs utilized 18 context-switches # 0.000 M/sec 0 CPU-migrations # 0.000 M/sec 82 page-faults # 0.000 M/sec 771,180,100 cycles # 3.528 GHz <not counted> stalled-cycles-frontend <not counted> stalled-cycles-backend 550,703,114 instructions # 0.71 insns per cycle 110,117,522 branches # 503.776 M/sec 5,009 branch-misses # 0.00% of all branches 0.219155248 seconds time elapsed 程序 t1 是一個 CPU bound 型,因為 task-clock-msecs 接近 1
對 t1 進行調優應該要找到熱點 ( 即最耗時的代碼片段 ),再看看是否能夠提高熱點代碼的效率。
通過指定 -e 選項,您可以改變 perf stat 的缺省事件 ( 關於事件,可以通過 perf list 來查看 )。假如您已經有很多的調優經驗,可能會使用 -e 選項來查看您所感興趣的特殊的事件。
有些程序慢是因為計算量太大,其多數時間都應該在使用 CPU 進行計算,這叫做 CPU bound 型;有些程序慢是因為過多的 IO,這種時候其 CPU 利用率應該不高,這叫做 IO bound 型;對於 CPU bound 程序的調優和 IO bound 的調優是不同的。
命令2:perf top實時顯示系統/進程的性能統計信息
默認性能事件“cycles CPU周期數”進行全系統的性能剖析
常見的參數如下:
1
2
3
4
|
-p:指定進程PID
-t:指定線程的TID
-a:分析整個系統的性能(默認)
-d:界面刷新周期,默認是
2
秒
|
結果輸出中,比例是該符號引發的性能時間在整個監測域中占的比例,通常稱為熱度。
samples pcnt function DSO _______ _____ ______________________________________________________________________________________ _________ 61.00 19.4% native_write_msr_safe [kernel] 18.00 5.7% JVM_InternString libjvm.so 17.00 5.4% find_busiest_group [kernel] 17.00 5.4% _spin_lock [kernel] 12.00 3.8% dev_hard_start_xmit [kernel] 11.00 3.5% tg_load_down [kernel] 9.00 2.9% futex_wake [kernel] 8.00 2.5% do_futex [kernel] 7.00 2.2% load_balance_fair [kernel] 7.00 2.2% weighted_cpuload [kernel] 7.00 2.2% update_cfs_shares [kernel] 7.00 2.2% JVM_LatestUserDefinedLoader libjvm.so 6.00 1.9% update_cfs_load [kernel] 5.00 1.6% _ZN16SystemDictionary30resolve_instance_class_or_nullE12symbolHandle6HandleS1_P6Thread libjvm.so 5.00 1.6% br_sysfs_delbr [bridge] 5.00 1.6% futex_wait
使用 perf stat 的時候,往往您已經有一個調優的目標。比如程序 t1。也有些時候,只是發現系統性能無端下降,並不清楚究竟哪個進程成為了貪吃的 hog。
此時需要一個類似 top 的命令,列出所有值得懷疑的進程,從中找到需要進一步審查的家伙。
Perf top 用於實時顯示當前系統的性能統計信息。該命令主要用來觀察整個系統當前的狀態,比如可以通過查看該命令的輸出來查看當前系統最耗時的內核函數或某個用戶進程。
讓再設計一個例子來演示:
//t2.c main(){ int i; while(1) i++; }
編譯:gcc -o t2 -g t2.c
運行這個程序后, 運行perf top來看看:
PerfTop: 705 irqs/sec kernel:60.4% [1000Hz cycles] -------------------------------------------------- sampl pcnt function DSO 1503.00 49.2% t2 72.00 2.2% pthread_mutex_lock /lib/libpthread-2.12.so 68.00 2.1% delay_tsc [kernel.kallsyms] 55.00 1.7% aes_dec_blk [aes_i586] 55.00 1.7% drm_clflush_pages [drm] 52.00 1.6% system_call [kernel.kallsyms] 49.00 1.5% __memcpy_ssse3 /lib/libc-2.12.so 48.00 1.4% __strstr_ia32 /lib/libc-2.12.so 46.00 1.4% unix_poll [kernel.kallsyms] 42.00 1.3% __ieee754_pow /lib/libm-2.12.so 41.00 1.2% do_select [kernel.kallsyms] 40.00 1.2% pixman_rasterize_edges libpixman-1.so.0.18.0 37.00 1.1% _raw_spin_lock_irqsave [kernel.kallsyms] 36.00 1.1% _int_malloc /lib/libc-2.12.so ^C
很容易便發現 t2 是需要關注的可疑程序。
關於perf top具節的一些命令:
1)perf top -e cpu-clock: 查看CPU的使用
2)perf top -e faults : 查看 page faults
3)perf top -e block:block_rq_issue : 查看系統IO的請求
比如可以在發現系統IO異常時,可以使用該命令進行調查,就能指定到底是什么原因導致的IO異常。 block_rq_issue 表示 block_request_issue 就是IO請求數。其實從這些可以看出,分析和調查Linux上的各種性能問題,需要我們對Linux內核有比較多的了解,不然恐怕是無從下手的。
命令3:perf record/report記錄一段時間內系統/進程的性能事件
默認在當前目錄下生成數據文件:perf.data 。report讀取生成的perf.data文件,-i參數指定路徑
使用 top 和 stat 之后,您可能已經大致有數了。要進一步分析,便需要一些粒度更細的信息。比如說您已經斷定目標程序計算量較大,也許是因為有些代碼寫的不夠精簡。那么面對長長的代碼文件,究竟哪幾行代碼需要進一步修改呢?這便需要使用 perf record 記錄單個函數級別的統計信息,並使用 perf report 來顯示統計結果。
您的調優應該將注意力集中到百分比高的熱點代碼片段上,假如一段代碼只占用整個程序運行時間的 0.1%,即使您將其優化到僅剩一條機器指令,恐怕也只能將整體的程序性能提高 0.1%。俗話說,好鋼用在刀刃上,不必我多說了。
perf record -e cpu-clock ./t1
perf report
perf report 輸出結果:
Events: 229 cpu-clock
100.00% t1 t1 [.] longa
不出所料,hot spot 是 longa( ) 函數。
但,代碼是非常復雜難說的,t1 程序中的 foo1() 也是一個潛在的調優對象,為什么要調用 100 次那個無聊的 longa() 函數呢?但我們在上圖中無法發現 foo1 和 foo2,更無法了解他們的區別了。
我曾發現自己寫的一個程序居然有近一半的時間花費在 string 類的幾個方法上,string 是 C++ 標准,我絕不可能寫出比 STL 更好的代碼了。因此我只有找到自己程序中過多使用 string 的地方。因此我很需要按照調用關系進行顯示的統計信息。
使用 perf 的 -g 選項便可以得到需要的信息:
perf record -e cpu-clock -g ./t1
perf report
輸出結果:
Events: 270 cpu-clock
- 100.00% t1 t1 [.] longa - longa + 91.85% foo1 + 8.15% foo2
命令4:使用 tracepoint
當 perf 根據 tick 時間點進行采樣后,人們便能夠得到內核代碼中的 hot spot。那什么時候需要使用 tracepoint 來采樣呢?
我想人們使用 tracepoint 的基本需求是對內核的運行時行為的關心,如前所述,有些內核開發人員需要專注於特定的子系統,比如內存管理模塊。這便需要統計相關內核函數的運行情況。另外,內核行為對應用程序性能的影響也是不容忽視的:
以之前的遺憾為例,假如時光倒流,我想我要做的是統計該應用程序運行期間究竟發生了多少次系統調用。在哪里發生的?
下面我用 ls 命令來演示 sys_enter 這個 tracepoint 的使用:
root@ubuntu-test:~# perf stat -e raw_syscalls:sys_enter ls bin libexec off perf.data.old t1 t3 tutong.iso bwtest minicom.log perf.data pktgen t1.c t3.c Performance counter stats for 'ls': 111 raw_syscalls:sys_enter 0.001557549 seconds time elapsed
這個報告詳細說明了在 ls 運行期間發生了多少次系統調用 ( 上例中有 111 次 )
主要參考文章:
Perf -- Linux下的系統性能調優工具,第 1 部分Perf -- Linux下的系統性能調優工具,第 2 部分 perf學習-linux自帶性能分析工具 Perf使用教程 Linux 性能優化工具 perf top
http://blog.csdn.net/bluebeach/article/details/5912062
http://lenky.info/2012/10/31/perf%E5%88%9D%E8%AF%95%E7%94%A8