使用 gprof 进行简单程序的性能分析
使用gprof
进行简单程序的性能分析
简介
gprof
产生程序运行时候的函数调用关系,包括调用次数,函数运行时间,调用关系等,可以帮助程序员分析程序的运行流程以及性能瓶颈,以达到编写更高效程序的目的。
使用方法
gprof
的使用方法很简单,大致分为以下几个步骤。
使用
-pg
参数编译可执行程序。运行可执行程序。
使用
gprof
生成分析结果。
例子
我们编写一个程序来演示一下如何使用gprof
。
程序的源码如下(程序没有特殊含义,只是为了演示gprof
的使用):
使用-pg
参数编译程序:
得到可执行程序main
。
运行main
程序。
程序运行结束后会产生一个gmon.out
文件。
使用gprof
生成分析结果。
此时我们使用了参数-b
和-a
,后续会介绍这两个参数。
注意:这里使用的是main
,而不是gmon.out
。
此时终端输出分析结果。(也可以使用gprof main > filename
将输出重定向到文件便于分析)。
输出结果可以分为两个部分,第一个部分如下:
上一部分给出了分析结果,下面的部分是对分析结果的解释。
可以看出,分析结果给出了:
函数运行时间百分比
函数运行的总时间(不包括子函数)
函数运行总时间(包括子函数)
调用次数
函数平均调用时间(不包括子函数)
函数平均调用时间(包括子函数)
函数名称
整个列表默认按照函数运行总时间(包括子函数)排序。
从上面的结果可以得出一些结论:
最耗时的调用为 xor_calc(),调用次数为 8 次,总时间为 35.73s,平均调用一次的时间为 4.47s,占总时间的 100.5%(此处统计有误,猜测是浮点数精度引起)。
调用 time_cost1()和 time_cost2()分别耗时 13.40s 和 22.33s,其中包括调用子函数的时间,如果除去子函数调用,则为 0s。
接下来看第二个部分:调用图。
和第一部分一样,上面部分是分析结果,下面部分是说明。
这部分结果给出了函数的调用关系。
左侧为函数唯一标号(由gprof
自动生成),右侧的每一列代表:此函数与其子函数消耗的时间战程序运行时间的百分比、此函数消耗时间、子函数消耗时间、调用次数、调用关系。
举例说明,我们看第 4 行的内容:
可以得出以下结果:
函数标号为 4,从右侧可以看出,4 标号代表函数
time_cost2
占用运行总时间 62.5%
调用关系为
main->time_cost2->xor_calc
main
自身消耗 0s,子函数消耗 22.33stime_cost2
自身消耗 0s,子函数消耗 22.33sxor_calc
自身消耗 22.33s,子函数消耗 0sxor_calc
一共被调用 8 次,此处被调用 5 次。
从以上结果可以大致分析出程序的性能瓶颈是在xor_calc
函数(事实上只有此函数耗时)。
常用参数
-b
:这也是我们常用参数之一,此参数会省略冗长的列信息解释,如果不加此参数,在输出分析结果表格的同时,还会输出每一列的详细解释,如果已经熟悉gprof
工具的使用,就可以使用此参数使输出更加简洁。
-C
:此参数输出每个函数的调用次数,-C
后面也可以跟上函数名称(编译后的名称)来打印指定函数的调用次数。上节的例子使用-C
参数的输出为:
-p
:仅打印性能分析结果,不打印调用图。
-q
:仅打印调用图,不打印性能分析结果。
-a
:不打印“私有函数”,这里的“私有”是指static
修饰的局部函数。
以上为几个常用参数,想要了解更详细的参数介绍,可以使用man
查看:
需要注意的地方
gprof
可以用来分析程序运行的情况,但是有一些地方需要注意。
递归的处理
gprof
以函数为单位,所以递归调用会被认为是在同一函数内部。计时并不精确
gprof
使用秒作为计时单位(虽然后面有小数点,但也最多精确到毫秒),所以对时间的统计并不准确,比如对一些执行时间短的函数直接计算为运行时间为 0。虽然计时并不精确,但各函数调用的时间比例大致准确,所以对程序优化仍有很强的参考意义。不支持多线程
gprof
只能分析主线程的函数调用情况,对于其他线程无能为力。不过这个问题通常可以通过编写单元测试,对程序的每一部分分别做性能分析。
gprof
对于一般小程序的性能分析差不过够用了,如果需要更专业的工具,可以试试valgrind
。valgrind
中的callgrind
相比gprof
更精细、更专业。
版权声明: 本文为 InfoQ 作者【SkyFire】的原创文章。
原文链接:【http://xie.infoq.cn/article/8e0a743bb2b6f7367559b9757】。文章转载请联系作者。
评论