(转)gprof-性能分析工具


gprof的使用非常简单,在编译链接的时候加上"-pg"选项,然后按照正常方式运行程序,如果程序正常退出,一个名为gmon.out将会产生。使用gprof可查看gmon.out中的统计结果:

gprof <options> [executable-file] [profile-data-file(s)……] [>outfile]

可通过man gprof 查看各选项含义,通常会加上"-b"选项禁止显示冗长的说明信息。
executable-file如果没有指定,则会默认为a.out。
profile-data-file可跟多个文件,若没有指定,默认gmon.out。
统计信息较多,最好重定向到outfile方便查看。
最终呈现的统计信息包括两张表:flat table和call graph。flat table列出了各个函数的运行时间(不包括子函数)及所占总运行时间的比率,函数的调用次数;call graph还包括函数之间的调用关系,详细列出了每个函数在它的各个子函数上所耗费的时间。
下面一个简单的例子说明一下两张表中各项的含义:
待分析的程序源码:test.cpp
#define MAX 10000000
void f()
{
    long long sum = 0;
    for (long long i = 0; i < MAX; i++)
        sum += i;
}
void g()
{
    long long sum = 0;
    for (long long i = 0; i < MAX; i++)
        sum += i;
    f();
}
int main()
{
    long long sum = 0;
    for (long long i = 0; i < MAX; i++)
        sum += i;
    f();
    g();

    return 0;
}
编译:g++ -g -Wall -pg -o test test.cpp
运行:./test。生成gmon.out文件
查看:gprof -b test gmon.out,输出如下:
Flat profile:

Each sample counts as 0.01 seconds.
  %   cumulative   self                          self         total           
 time   seconds   seconds    calls  ms/call  ms/call  name    
 50.00      0.05     0.05        2    25.00    25.00  f()
 30.00      0.08     0.03        1    30.00    55.00  g()
 20.00      0.10     0.02                             main

            Call graph


granularity: each sample hit covers 4 byte(s) for 10.00% of 0.10 seconds

index % time    self  children    called     name
                                                 <spontaneous>
[1]    100.0    0.02    0.08                 main [1]
                0.03    0.03       1/1           g() [2]
                0.03    0.00       1/2           f() [3]
-----------------------------------------------
                0.03    0.03       1/1           main [1]
[2]     55.0    0.03    0.03       1         g() [2]
                0.03    0.00       1/2           f() [3]
-----------------------------------------------
                0.03    0.00       1/2           g() [2]
                0.03    0.00       1/2           main [1]
[3]     50.0    0.05    0.00       2         f() [3]
-----------------------------------------------

Index by function name

   [3] f()                     [2] g()                     [1] main

Flat profile:
% time:各个函数占用的时间比率(不包括子函数),这一列加起来应该为100%。也就是以%形式展示 self seconds 列
cumulative seconds:累积时间,当前行减去上一行即为当前函数耗费时间
self seconds:函数自身(不包括子函数)在程序生命周期内全部调用消耗的时间,单位:秒 = (self ms/call) * calls / 1000
calls:函数在程序生命期内的总的调用次数。(注!递归调用不计数;多次递归调用的时间算在单次的调用时间里面)
self ms/call:函数自身(不包括子函数)每次调用消耗的时间,单位:毫秒
total ms/call:函数自身(包括子函数)每次调用消耗的时间,单位:毫秒。也就是从进入函数到离开函数的时间(不包括sleep的时间)
name:函数名

Flat profile数据分析:
1) 看%time列, 或者 "self ms/call"列, 这里消耗时间最多的函数就是最耗费CPU的函数了. 也是最值得优化的函数了. (消耗仅统计函数自身的代码消耗, 不统计子函数的消耗)
2) 看"total ms/call"列, 找到包含子函数在内最耗时间的函数
3) 从"self ms/call"列和"total ms/call"列对比可知, 如果self ms/call列的值很小:0.00, 则推测子函数消耗时间很多

call graph:
每个函数都分配了一个index,index按升序排列,一个函数对应一个entry,两个entry之间用虚线隔开。
在每个entry中,以[index]起头的行称为primary line。primary line上面的行称为caller's line,列举的是调用该函数的函数;下面的行subroutine's line列举的是该函数调用的子函数。这三种line的各项名称虽然相同,但有着截然不同的含义。
以下都以第二个entry为例说明:
primary line
index % time    self  children    called     name
[2]     55.0    0.03    0.03       1         g() [2]
%time:g()耗费的时间比率。该比率包括了调用的f(),因此各个entry的该项数字加起来不等于100%。
self:同flat table的self seconds。
children:f()耗费的时间。下面的subroutines's line 的self项和children项之和应等于该数值。
called:只被调用了一次。

subroutine's line
index % time    self  children    called     name
                0.03    0.00       1/2           f() [3]
self:f()被g()调用过程中,f()的耗费时间0.03s。
children:f()被g()调用过程中,f()中的子函数耗费时间为0s。
called:f()一共被调用了2次,其中有1次被g()调用。

caller's line
index % time    self  children    called     name
                0.03    0.03       1/1           main [1]
self:g()被main()调用过程中,g()的耗费时间。
children:g()被main()调用过程中,g()中的子函数耗费的时间。
called:g()一共被调用了1次,其中1次是被main()调用的。

gprof的基本原理
类似于gdb,gprof需要对待分析的程序做一些改动,因此在程序编译的时候需要加上"-pg"选项,如果程序的某个模块在编译的时候没有加上"-pg",则该模块的函数会被排除在统计范围之外。比如想要查看库函数的profiling,则需在链接库函数的时候用“-lc_p"代替”-lc"(gprof是各个类UNIX的标准工具,系统自带的链接库通常有两个版本,它们的区别在于编译的时候是否加上了"-pg"。用-lc_p等于告诉编译器选择加上了"-pg"的那个版本)。
加上"-pg"选项后,程序的入口会于main()之前调用monstartup(),主要是申请内存存储接下来获取的统计信息。
在每个函数中会调用mcount(),主要是在函数的堆栈中查询父函数和子函数的地址并保存下来。
最后会在程序退出前调用_mcleanup(),将统计结果保存到gmon.out中,并完成清除工作。
gprof统计各个函数的运行时间是采用的抽样的方法,周期性的查看Program counter指向哪一个函数的地址段,并把结果以直方图的形式保存下来。

注意:
1,有人建议在编译时不要加上"-g"选项,因为这样可能会影响分析结果。
2,通常gprof的采样周期是0.01s,统计项越接近这个值误差可能越大。若函数的运行时间低于0.01S,统计值会显示为0。
3,多线程下,gprof只能采集主线程性能数据。原因是gprof采用ITIMER_PROF信号,在多线程内,只有主线程才能响应该信号。解决的关键是让各个线程响应ITIMER_PROF信号,就是重写pthread_create函数。
4,一般gprof只能查看用户函数信息。如果想查看库函数的信息,需要在编译是再加入"-lc_p"编译参数代替"-lc"编译参数,这样程序会链接libc_p.a库,才可以产生库函数的profiling信息。
5,gprof只能在程序正常结束退出之后才能生成程序测评报告,原因是gprof通过在atexit()里注册了一个函数来产生结果信息,任何非正常退出都不会执行atexit()的动作,所以不会产生gmon.out文件。如果你的程序是一个不会退出的服务程序,那就只有修改代码来达到目的。如果不想改变程序的运行方式,可以添加一个信号处理函数解决问题(这样对代码修改最少),例如:
static void sighandler( int sig_no )
{
exit(0);
}
signal( SIGUSR1, sighandler );
当使用kill -USR1 pid 后,程序退出,生成gmon.out文件。
6,oprofile也是一个开源的profiling工具,它使用硬件调试寄存器来统计信息,可以对内核态程序进行profiling。
7,函数耗时、调用关系图形化工具:graphviz gprof2dot.py

常用选项介绍:[symspec表示需要加入或排除的函数名,和gdb指定断点时的格式相同]
输出相关:
-A[symspec]或--annotated-source[=symspec]:进行源码关联,只关联symspec指定的函数,不指定为全部关联。
-I dirs或--directory-path=dirs:添加搜索源码的文件夹,修改环境变量GPROF_PATH也可以。
-p[symspec]或--flat-profile[=symspec]:默认选项,输出统计信息,只统计symspec指定的函数,不指定为全部统计。
-P[symspec]或--no-flat-profile[=symspec]:排除统计symspec指定的函数
-q[symspec]或--graph[=symspec]:默认选项,输出函数调用信息,只统计symspec指定的函数,不指定为全部统计。
-Q[symspec]或--no-graph[=symspec]:排除统计symspec指定的函数
-b或--brief:不输出对各个参数含义的解释;
分析相关:
-a或--no-static:定义为static的函数将不显示,函数的被调用次数将被计算在调用它的不是static的函数中;
-m num或--min-count=num:不显示被调用次数小于num的函数;
-z或--display-unused-functions:显示没有被调用的函数;

转载于:https://my.oschina.net/u/3485339/blog/900412

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值