性能优化之C++ Profiler

37 篇文章 0 订阅

最近在做有关性能优化方面的东西,其中使用到了Profiler,就目前来看除了glibc中自带的gprof以外,还有一个使用的比较广泛的由google开发的GooglePerformance tools,在这篇博文中,我们就来学学gprof这个性能调试工具吧,在介绍gprof之前,我们有必要来思考下我们优化的性能主要包括哪些部分,这部分内容可以参考这篇博文http://coolshell.cn/articles/7490.html,简要地说性能主要包括了:1)吞吐量,2)时延;一般来说,一个系统的性能受到这两个条件的约束,缺一不可。比如,我的系统可以顶得住一百万的并发,但是系统的延迟是2分钟以上,那么,这个一百万的负载毫无意义。系统延迟很短,但是吞吐量很低,同样没有意义。所以,一个好的系统的性能测试必然受到这两个条件的同时作用。 有经验的朋友一定知道,这两个东西的一些关系:Throughput越大,Latency会越差。因为请求量过大,系统太繁忙,所以响应速度自然会低。Latency越好,能支持的Throughput就会越高。因为Latency短说明处理速度快,于是就可以处理更多的请求。这部分内容是完全参考了cool shell的博文,好了,介绍到此,接下来,我们就来看看gprof这个工具吧,测试代码如下: 

[cpp]  view plain  copy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. int worker1()  
  4. {  
  5.     int i=0,j=0;  
  6.     while(i++ < 4000000)  
  7.         j+=i;  
  8. }  
  9.   
  10. int worker2()  
  11. {  
  12.     int i=0,j=0;  
  13.     while(i++ < 400000)  
  14.         j+=i;  
  15. }  
  16.   
  17. int main(int argc,char** argv)  
  18. {  
  19.     int num = 0;  
  20.     if(argc <2)  
  21.     {  
  22.         printf("usage %s num\n",argv[0]);  
  23.         exit(-1);  
  24.     }  
  25.     num = atoi(argv[1]);  
  26.     while(num--)  
  27.     {  
  28.         worker1();  
  29.         worker2();  
  30.     }  
  31. }  

以上是测试代码,首先通过g++ -o Test Test.cpp -pg -lc,来编译,编译完成时会生成gmon.out文件,有了这个文件,我们就可以通过gprof Test gmon.out +编译选项来查看程序的性能情况

1)选项 -p

[cpp]  view plain  copy
  1. Flat profile:  
  2.   
  3. Each sample counts as 0.01 seconds.  
  4.   %   cumulative   self              self     total  
  5.  time   seconds   seconds    calls  ms/call  ms/call  name  
  6.  90.65     98.64    98.64    10000     9.86     9.86  worker1()  
  7.   9.35    108.81    10.17    10000     1.02     1.02  worker2()  

从上述代码中,可以看出woker1的操作耗时基本上是worker2的10倍,并且最主要的耗时是在叠加计算上,

2)选项 -q

[cpp]  view plain  copy
  1.                      Call graph (explanation follows)  
  2.   
  3.   
  4. granularity: each sample hit covers 4 byte(s) for 0.01% of 108.81 seconds  
  5.   
  6. index % time    self  children    called     name  
  7.                                                  <spontaneous>  
  8. [1]    100.0    0.00  108.81                 main [1]  
  9.                98.64    0.00   10000/10000       worker1() [2]  
  10.                10.17    0.00   10000/10000       worker2() [3]  
  11. -----------------------------------------------  
  12.                98.64    0.00   10000/10000       main [1]  
  13. [2]     90.7   98.64    0.00   10000         worker1() [2]  
  14. -----------------------------------------------  
  15.                10.17    0.00   10000/10000       main [1]  
  16. [3]      9.3   10.17    0.00   10000         worker2() [3]  
  17. -----------------------------------------------  

这个选项相比于-p,其更加详细,从上面可以看出main函数基本上的耗时都在函数调用上,而worker1的耗时是worker2的10倍,

从上述代码测试中,有一个局限性:只能测试用户自行写的程序,而对于通过共享库而使用的一个函数是否也可以检查出来呢,目前在希望从共享库(包括 C 库 libc.a)中获得剖析信息,就需要使用-pg 来编译这些库。幸运的是,很多发行版都提供了已经启用代码剖析支持而编译的 C 库版本(libc_p.a)。下面我们就来看看如何通过共享库来获取程序的各个部分的性能情况,代码如下:

[cpp]  view plain  copy
  1. #include <stdio.h>  
  2. int a(void) {  
  3.   sleep(1);  
  4.   return 0;  
  5. }  
  6. int b(void) {  
  7.   sleep(4);  
  8.   return 0;  
  9. }  
  10. int main(int argc, char** argv)  
  11. {  
  12.    int iterations;  
  13.    if(argc != 2)  
  14.    {  
  15.       printf("Usage %s <No of Iterations>\n", argv[0]);  
  16.       exit(-1);  
  17.    }  
  18.    else  
  19.       iterations = atoi(argv[1]);  
  20.    printf("No of iterations = %d\n", iterations);  
  21.    while(iterations--)  
  22.    {  
  23.       a();  
  24.       b();  
  25.    }  
  26. }  

测试结果(g++ -o Test Test.cpp -pg -lc_p;./Test 30)

[cpp]  view plain  copy
  1. Flat profile:  
  2. Each sample counts as 0.01 seconds.  
  3.  no time accumulated  
  4.   %   cumulative   self              self     total  
  5.  time   seconds   seconds    calls  Ts/call  Ts/call  name  
  6.   0.00      0.00     0.00      120     0.00     0.00  sigprocmask  
  7.   0.00      0.00     0.00       61     0.00     0.00  __libc_sigaction  
  8.   0.00      0.00     0.00       61     0.00     0.00  sigaction  
  9.   0.00      0.00     0.00       60     0.00     0.00  nanosleep  
  10.   0.00      0.00     0.00       60     0.00     0.00  sleep  
  11.   0.00      0.00     0.00       30     0.00     0.00  a  
  12.   0.00      0.00     0.00       30     0.00     0.00  b  
  13.   0.00      0.00     0.00       21     0.00     0.00  _IO_file_overflow  
  14.   0.00      0.00     0.00        3     0.00     0.00  _IO_new_file_xsputn  
  15.   0.00      0.00     0.00        2     0.00     0.00  _IO_new_do_write  
  16.   0.00      0.00     0.00        2     0.00     0.00  __find_specmb  
  17.   0.00      0.00     0.00        2     0.00     0.00  __guard_setup  
  18.   0.00      0.00     0.00        1     0.00     0.00  _IO_default_xsputn  
  19.   0.00      0.00     0.00        1     0.00     0.00  _IO_doallocbuf  
  20.   0.00      0.00     0.00        1     0.00     0.00  _IO_file_doallocate  
  21.   0.00      0.00     0.00        1     0.00     0.00  _IO_file_stat  
  22.   0.00      0.00     0.00        1     0.00     0.00  _IO_file_write  
  23.   0.00      0.00     0.00        1     0.00     0.00  _IO_setb  
  24.   0.00      0.00     0.00        1     0.00     0.00  ____strtol_l_internal  
  25.   0.00      0.00     0.00        1     0.00     0.00  ___fxstat64  
  26.   0.00      0.00     0.00        1     0.00     0.00  __cxa_atexit  
  27.   0.00      0.00     0.00        1     0.00     0.00  __errno_location  
  28.   0.00      0.00     0.00        1     0.00     0.00  __new_exitfn  
  29.   0.00      0.00     0.00        1     0.00     0.00  __strtol_internal  
  30.   0.00      0.00     0.00        1     0.00     0.00  _itoa_word  
  31.   0.00      0.00     0.00        1     0.00     0.00  _mcleanup  
  32.   0.00      0.00     0.00        1     0.00     0.00  atexit  
  33.   0.00      0.00     0.00        1     0.00     0.00  atoi  
  34.   0.00      0.00     0.00        1     0.00     0.00  exit  
  35.   0.00      0.00     0.00        1     0.00     0.00  flockfile  
  36.   0.00      0.00     0.00        1     0.00     0.00  funlockfile  
  37.   0.00      0.00     0.00        1     0.00     0.00  main  
  38.   0.00      0.00     0.00        1     0.00     0.00  mmap  
  39.   0.00      0.00     0.00        1     0.00     0.00  moncontrol  
  40.   0.00      0.00     0.00        1     0.00     0.00  new_do_write  
  41.   0.00      0.00     0.00        1     0.00     0.00  printf  
  42.   0.00      0.00     0.00        1     0.00     0.00  setitimer  
  43.   0.00      0.00     0.00        1     0.00     0.00  vfprintf  
  44.   0.00      0.00     0.00        1     0.00     0.00  write  

尽管 profiler 已经记录了每个函数被调用的确切次数,但是为这些函数记录的时间(实际上是所有函数)都是 0.00。这是因为 sleep 函数实际上是执行了一次对内核空间的调用,从而将应用程序的执行挂起了,然后有效地暂停执行,并等待内核再次将其唤醒。由于花在用户空间执行的时间与花在内核中睡眠的时间相比非常小,因此就被取整成零了。其原因是 gprof 仅仅是通过以固定的周期对程序运行时间 进行采样测量来工作的。因此,当程序不运行时,就不会对程序进行采样测量。

总结
      尽管 gprof 存在上面的限制,但是它对于优化代码来说依然是个非常有用的工具,如果您的代码大部分是用户空间 CPU 密集型的,它的用处就更加明显。首先使用time 来运行程序从而判断 gprof 是否能产生有用信息是个好主意。好了,本篇博文到此结束,下篇我们会通过几个实例来进一步学习使用gprof工具进行性能调优。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值