使用oprofile分析性能瓶颈

使用oprofile分析性能瓶颈


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上已经移植好了,包括用户空间的工具集软件包,亦可用矣。 

一个测试用的内核,已经打开   oprofile   ,位于   http://people.openrays.org/~comcat/godson/vmlinux-2.6.18-oprofile 

用户空间工具集deb   包位于:   http://people.openrays.org/~comcat/godson/oprofile-0.9.2/ 


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% 
http://www.lemote.com/bbs/viewthread.php?tid=7973

你可能感兴趣的:(使用oprofile分析性能瓶颈)