使用oprofile分析性能瓶頸(1)

时间:2021-02-08 00:06:07

使用oprofile分析性能瓶頸(1)


1. 概述

 

oprofile  Linux 平台上,類似 INTEL VTune 的一個功能強大的性能分析工具。

 

其支持兩種採樣(sampling)方式:基於事件的採樣(event based)和基於時間的採樣(time based)

 

基於事件的採樣是oprofile只記錄特定事件(比如L2 cache miss)的發生次數,當達到用戶設定的定值時oprofile 就記錄一下(採一個樣)。這種方式需要CPU 內部有性能計數器(performace counter)。現代CPU內部一般都有性能計數器,龍芯2E內部亦內置了2個性能計數器。

 

基於時間的採樣是oprofile 借助OS 時鐘中斷的機制,每個時鐘中斷 oprofile 都會記錄一次採一次樣)。引入的目的在於,提供對沒有性能計數器 CPU 的支持。其精度相對於基於事件的採樣要低。因為要借助 OS時鐘中斷的支持,對禁用中斷的代碼oprofile不能對其進行分析。

 

oprofile Linux 上分兩部分,一個是內核模塊(oprofile.ko),一個為用戶空間的守護進程(oprofiled)。前者負責訪問性能計數器或者注冊基於時間採樣的函數(使用register_timer_hook注冊之,使時鐘中斷處理程序最后執行profile_tick 時可以訪問之),並採樣置於內核的緩沖區內。后者在后台運行,負責從內核空間收集數據,寫入文件。

 

 

2. oprofile 的安裝

 

以龍芯2E平台為例,要使用oprofile 首先得採用打開oprofile支持的內核啟動。然后安裝下面3個軟件包:oprofile, oprofile-common, oprofile-gui,其中核心軟件包是oprofile-common,其包括以下工具集:

 

    /usr/bin/oprofiled         守護進程

    /usr/bin/opcontrol         控制前端,負責控制與用戶交互,用得最多    

    /usr/bin/opannotate       根據搜集到的數據,在源碼或者匯編層面上注釋並呈現給用戶

    /usr/bin/opreport         生成二進制鏡像或符號的概覽

    /usr/bin/ophelp           列出oprofile支持的事件

    /usr/bin/opgprof          生成gprof格式的剖析數據

    ...

 

 

目前oprofile 在龍芯2E上已經移植好了,包括用戶空間的工具集軟件包,亦可用矣。

 

 

3. oprofile 快速上手

 

a. 初始化

 

    opcontrol --init

 

    該命令會加載oprofile.ko模塊,mount oprofilefs。成功后會在/dev/oprofile/目錄下導出一些文件和目錄如: cpu_type, dump, enable, pointer_size, stats/

 

b. 配置

 

    主要設置計數事件和樣本計數,以及計數的CPU模式(用戶態、核心態)

   

    opcontrol --setup --event=CYCLES:1000::0:1

 

    則是設置計數事件為CYCLES,即對處理器時鐘周期進行計數

    樣本計數為1000,即每1000個時鐘周期,oprofile 取樣一次。

    處理器運行於核心態則不計數

    運行於用戶態則計數

 

    --event=name:count:unitmask:kernel:user

 

    name:   event name, e.g. CYCLES or ICACHE_MISSES

    count:   reset counter value e.g. 100000

    unitmask: hardware unit mask e.g. 0x0f

    kernel:   whether to profile kernel: 0 or 1

    user:   whether to profile userspace: 0 or 1

 

c. 啟動

 

    opcontrol --start

 

 

d. 運行待分析之程序

 

    ./ffmpeg -c cif -vcodec mpeg4 -i /root/paris.yuv paris.avi

 

e. 取出數據

 

    opcontrol --dump

    opcontrol --stop

 

f. 分析結果

 

    opreport -l ./ffmpeg

 

 

則會輸出如下結果:

 

CPU: GODSON2E, speed 0 MHz (estimated)

Counted CYCLES events (Cycles) with a unit mask of 0x00 (No unit mask) count 10000

samples %     symbol name

11739   27.0148 pix_abs16_c

6052   13.9274 pix_abs16_xy2_c

4439   10.2154 ff_jpeg_fdct_islow

2574     5.9235 pix_abs16_y2_c

2555     5.8798 dct_quantize_c

2514     5.7854 pix_abs8_c

2358     5.4264 pix_abs16_x2_c

1388     3.1942 diff_pixels_c

964     2.2184 ff_estimate_p_frame_motion

852     1.9607 simple_idct_add

768     1.7674 sse16_c

751     1.7283 ff_epzs_motion_search

735     1.6914 pix_norm1_c

619     1.4245 pix_sum_c

561     1.2910 mpeg4_encode_blocks

558     1.2841 encode_thread

269     0.6190 put_no_rnd_pixels16_c

255     0.5868 dct_unquantize_h263_inter_c

 

......

 

 

4. 例子

 

oprofile 可以分析處理器周期、TLB 失誤、分支預測失誤、緩存失誤、中斷處理程序,等等。你可以使用 opcontrol --list-events 列出當前處理器上可監視事件列表。

 

下面分析一個編寫不當的例子:

 

 

[帶有cache問題的代碼cache.c]

+++++++++++++++++++++++++++++++++++++++++++++++

 

int matrix[2047][7];

 

 

void bad_access()

{

  int k, j, sum = 0;

 

  for(k = 0; k < 7; k++)

    for(j = 0; j < 2047; j++)

        sum += matrix[j][k] * 1024;

 

}

 

int main()

{

    int i;

 

    for(i = 0; i< 100000; i++)

        bad_access();

 

    return 0;

 

}

 

+++++++++++++++++++++++++++++++++++++++++++++++

 

 

編譯之: gcc -g cache.c -o cache

 

 

使用oprofile 分析之:

 

opcontrol --init

 

opcontrol --setup --event=DCACHE_MISSES:500::0:1

 

opcontrol --start && ./cache && opcontrol --dump && opcontrol --stop

 

 

使用 opannotate 分析結果為:

 

/*

* Command line: opannotate --source ./cachee

*

* Interpretation of command line:

* Output annotated source file with samples

* Output all files

*

* CPU: GODSON2E, speed 0 MHz (estimated)

* Counted ICACHE_MISSES events (Instruction Cache misses number ) with a unit mask of 0x00 (No unit mask) count 500

*/

/*

* Total samples for file : "/comcat/test/pmc.test/cachee.c"

*

*   34 100.000

*/

 

 

          :int matrix[2047][7];

          :

          :void bad_access()

          :{ /* bad_access total:   33 97.0588 */

          :   int k, j, sum = 0;

          :

          :   for(k = 0; k < 7; k++)

  33 97.0588 :     for(j = 0; j < 2047; j++)

          :         sum += matrix[j][k] * 1024;

          :

          :}

          :

          :int main()

          :{ /* main total:     1 2.9412 */

          :   int i;

          :

  1 2.9412 :   for(i = 0; i< 10000; i++)

          :           bad_access();

          :

          :   return 0;

          :

          :}

          :

 

 

opreport 解析的結果為:

 

GodSonSmall:/comcat/test/pmc.test# opreport -l ./cache

CPU: GODSON2E, speed 0 MHz (estimated)

Counted ICACHE_MISSES events (Instruction Cache misses number ) with a unit mask of 0x00 (No unit mask) count 500

samples %     symbol name

33     97.0588 bad_access

1       2.9412 main

 

可以看到bad_access() cache miss 事件的樣本共有33個,占總數的97%

 

 

改進 bad_access()  good_access() 后:

 

void good_access()

{

  int k, j, sum = 0;

  for(k = 0; k < 2047; k++)

    for(j = 0; j < 7; j++)

        sum += matrix[k][j] * 1024;

 

}

 

 

CPU: GODSON2E, speed 0 MHz (estimated)

Counted ICACHE_MISSES events (Instruction Cache misses number ) with a unit mask of 0x00 (No unit mask) count 500

samples %     symbol name

22     95.6522 good_access

1       4.3478 main

 

可以看到改进后 cache miss 事件的样本减少为22个,占总数的95%