设为首页 友情链接
在线留言 发表文章
加入收藏 广告联系

刺猬首页

| 专案技术 | 网络技术 | 图形图象 | 网络编程 | 网页设计 | 操作系统 | 服务器 | 技术白皮书 | 在线实验室 | 刺猬论坛 |
小说专版  | 数据库 | 设计赏析 | 存储频道 | 网络安全 | 私服架设 |  Solaris | 网站评估 | PC维护技巧 | 下载中心 | 博 客 |
专   题: | Linux | java | cisco | 防病毒 | 刀片 | SOA | iscsi | ASP.NET | SQL | Oracle |
您现在的位置: IT公社 IT community >> Linux专题 >> Linux 软件开发 >> 教程正文 用户登录 新用户注册
专 题 栏 目
最 新 热 门
最 新 推 荐
相 关 文 章
Linux下的硬件驱动——U…
Linux下的硬件驱动——U…
实战linux内核编译
Linux内核参数(二)
linux内核参数(一)
Linux 2.4.20 以后内核的…
菜鸟浅谈Linux内核编译过…
Linux操作系统之快速内存…
Linux操作系统的内存管理…
从红旗5.0说起——看Lin…
  用GNU profiler提高代码运行速度         
用GNU profiler提高代码运行速度
 

改进应用程序的性能是一项非常耗时耗力的工作,但是究竟程序中是哪些函数消耗掉了大部分执行时间,这通常都不是非常明显的。在本文中我们将学习如何使用 gprof 为 Linux ?? 平台上的用户空间和系统调用精确分析性能瓶颈。

    简介

    各种软件对于性能的需求可能会有很大的区别,但是很多应用程序都有非常严格的性能需求,这一点并不奇怪。电影播放器就是一个很好的例子:如果一个电影播放器只能以所需要速度的 75% 来播放电影,那么它几乎就没什么用处了。

    其他应用程序(例如视频编码)如果是耗时非常长的操作,最好以 “批处理” 任务的方式运行,此时启动一个作业,让其一直运行,然后我们就可以去干别的事情了。尽管这些类型的应用程序没有这种硬性性能指标的限制,但是提高速度仍然会带来很多好处,例如可以在给定的时间内可以对更多电影进行编码,在同样的时间内可以以更高的品质进行编码。

    通常,除了最简单的应用程序之外,对于其他应用程序来说,性能越好,这个应用程序的用处就越大,也就会越流行。由于这个原因,性能考虑是(也应该是)很多应用程序开发人员脑袋中的第一根弦。

    不幸的是,很多尝试让应用程序速度更快的努力都白费了,因为开发人员通常都是对自己的软件进行一些小型的优化,而没有去研究程序在更大的范围内是如何操作的。例如,我们可能会花费大量的时间来让某个特定函数的运行速度达到原来的两倍,这一点非常不错,但是如果这个函数很少被调用(例如打开文件),那么将这个函数的执行时间从 200ms 减少到 100ms,对于整个软件的总体执行时间来说并不会有太大的影响。

    有效地利用您的时间的方法是,尽量优化软件中被频繁调用的部分。例如,假设应用程序花了 50% 的时间在字符串处理函数上,如果可以对这些函数进行优化,提高 10% 的效率,那么应用程序的总体执行时间就会改进 5%.

    因此,如果希望能够有效地对程序进行优化,那么精确地了解时间在应用程序中是如何花费的,以及真实的输入数据,这一点非常重要。这种行为就称为代码剖析(code profiling)。本文将简要介绍 GNU 编译器工具包所提供的一种剖析工具,它的名字让人可以产生无限遐想,叫 GNU profiler(gprof)。本文主要面向那些开放源码软件开发工具的新手。

    gprof 来救援了

    在开始介绍如何使用 gprof 之前,需要首先了解一下在整个开发周期中,剖析应该在何处进行。通常来说,编写代码应该有 3 个目标,按照重要性的次序分别如下所示:

    保证软件可以正确地工作。这通常是开发过程的重点。通常,如果一个软件根本连我们期望它做的事情都实现不了,那么即使它运行速度非常快,也根本没有任何意义!显然,正确性在某些情况下可能并不是至关重要的;例如,如果一个电影播放器可以正确地播放 99% 的电影文件,但是偶然会有些显示问题,那它依然可以使用。但是通常来说,正确性要远远比速度更加重要。

    保证软件是可维护的。这实际上是第一个目标的一个子项。通常,如果软件编写得可维护性不好,那么即使它最开始时可以很好地工作,很快您(或其他人)在修正 bug 或添加新特性时可能也会破坏程序的正确性。

    让软件可以快速运行。这就是剖析的用武之地。当软件可以正确运行之后,我们就可以开始剖析的过程来帮助它更快地运行了。

    假设我们现在已经有了一个可以工作的应用程序,接下来让我们来看一下如何使用 gprof 来精确测量应用程序执行过程中时间都花费到什么地方去了,这样做的目的是了解一下在什么地方进行优化效果最佳。

    gprof 可以对 C、C++、Pascal 和 Fortran 77 应用程序进行剖析。本文中的例子使用的是 C.

    清单 1. 耗时的应用程序示例




#include <stdio.h>



int a(void) {

  int i=0,g=0;

  while(i++<100000)

  {

     g+=i;

  }

  return g;

}

int b(void) {

  int i=0,g=0;

  while(i++<400000)

  {

    g+=i;

  }

  return g;

}



int main(int argc, char** argv)

{

   int iterations;



   if(argc != 2)

   {

      printf("Usage %s <No of Iterations>\n", argv[0]);

      exit(-1);

   }

   else

      iterations = atoi(argv[1]);



   printf("No of iterations = %d\n", iterations);



   while(iterations--)

   {

      a();

      b();

   }

}


    正如我们从代码中可以看到的,这个非常简单的应用程序包括两个函数:a 和 b,它们都处于一个繁忙的循环中消耗 CPU 周期。main 函数中采用了一个循环来反复调用这两个函数。第二个函数 b 循环的次数是 a 函数的 4 倍,因此我们期望在对代码分析完之后,可以看出大概有 20% 的时间花在了 a 函数中,而 80% 的时间花在了 b 函数中。下面就开始剖析代码,并看一下我们的这些期望是否正确。

    启用剖析非常简单,只需要在 gcc 编译标志中加上 -pg 即可。编译方法如下:

    gcc example1.c -pg -o example1 -O2 -lc

    在编译好这个应用程序之后,可以按照普通方式运行这个程序:

    。/example1 50000

    当这个程序运行完之后,应该会看到在当前目录中新创建了一个文件 gmon.out.

    使用输出结果

    首先看一下 “flat profile”,我们可以使用 gprof 命令获得它,这需要为其传递可执行文件和 gmon.out 文件作为参数,如下所示:

    gprof example1 gmon.out -p

    这会输出以下内容:

    清单 2. flat profile 的结果




Flat profile:



Each sample counts as 0.01 seconds.

  %   cumulative   self              self     total

 time   seconds   seconds    calls  ms/call  ms/call  name

 80.24     63.85    63.85    50000     1.28     1.28  b

 20.26     79.97    16.12    50000     0.32     0.32  a


    从这个输出结果中可以看到,正如我们期望的一样,b 函数所花费的时间大概是 a 函数所花费的时间的 4 倍。真正的数字并不是十分有用;由于取整舍入错误,这些数字可能并不是非常精确。

    聪明的读者可能会注意到,很多函数调用(例如 printf)在这个输出中都没有出现。这是因为这些函数都是在 C 运行时库(libc.so)中的,(在本例中)它们都没有使用 -pg 进行编译,因此就没有对这个库中的函数收集剖析信息。稍后我们会回到这个问题上来。

    接下来我们希望了解的是 “call graph”,这可以通过下面的方式获得:

    gprof example1 gmon.out -q

    这会输出下面的结果。

    清单 3. Call graph




                     Call graph (explanation follows)

granularity: each sample hit covers 2 byte(s) for 0.01% of 79.97 seconds



index % time    self  children    called     name

                                                 <spontaneous>

[1]    100.0    0.00   79.97                 main [1]

               63.85    0.00   50000/50000       b [2]

               16.12    0.00   50000/50000       a [3]

-----------------------------------------------

               63.85    0.00   50000/50000       main [1]

[2]     79.8   63.85    0.00   50000         b [2]

-----------------------------------------------

               16.12    0.00   50000/50000       main [1]

[3]     20.2   16.12    0.00   50000         a [3]

-----------------------------------------------


    最后,我们可能会希望获得一个 “带注解的源代码” 清单,它会将源代码输出到应用程序中,并加上每个函数被调用了多少次的注释。

    要使用这种功能,请使用启用调试功能的标志来编译源代码,这样源代码就会被加入可执行程序中:

    gcc example1.c -g -pg -o example1 -O2 -lc

    像以前一样重新运行这个应用程序:

    。/example1 50000

    gprof 命令现在应该是:

    gprof example1 gmon.out -A

    这会输出下面的结果:

    清单 4. 带注释的源代码




*** File /home/martynh/profarticle/example1.c:

                #include <stdio.h>



       50000 -> int a(void) {

                  int i=0,g=0;

                  while(i++<100000)

                  {

                     g+=i;

                  }

                  return g;

                }

       50000 -> int b(void) {

                  int i=0,g=0;

                  while(i++<400000)

                  {

                    g+=i;

                  }

                  return g;

                }



                int main(int argc, char** argv)

       ##### -> {

                   int iterations;



                   if(argc != 2)

                   {

                      printf("Usage %s <No of Iterations>\n", argv[0]);

                      exit(-1);

                   }

                   else

                      iterations = atoi(argv[1]);



                   printf("No of iterations = %d\n", iterations);



                   while(iterations--)

                   {

                      a();

                      b();

                   }

                }







Top 10 Lines:



     Line      Count



        3      50000

       11      50000

Execution Summary:



        3   Executable lines in this file

        3   Lines executed

   100.00   Percent of the file executed



   100000   Total number of line executions

 33333.33   Average executions per line

Linux联盟收集整理

频道声明:本频道的文章除部分特别声明禁止转载的专稿外,可以自由转载.但请务必注明出出处和原始作者 文章版权归本频道与文章作者所有.对于被频道转载文章的个人和网站,我们表示深深的谢意。

原始作者:佚名 录入时间:2007-1-2 3:10:56
信息来源:不详 投稿信箱:itqoo@126.com
教程录入:itqoo    责任编辑:itqoo 
  • 上一个教程:

  • 下一个教程:
  • 【字体: 】【发表评论】【加入收藏】【告诉好友】【打印此文】【关闭窗口
      网友评论:(只显示最新10条。评论内容只代表网友观点,与本站立场无关!)
    - 关于我们 - 合作伙伴 - 友情链接 - 广告刊登 - 投稿热线 - 在线留言版权声明联系方式 -
    IT公社版权所有 粤ICP备05127012号
    Copyrigh@2005-2006 itqoo.com.Inc All Rights Reserved  推荐分辨率 1024*768
    联系站长:E-Mail:itqoo@126.com     MSN:urchincc@hotmail.com    QQ:点击这里给我发消息
    特别感谢:亿太网络提供空间支持