gprof原理与缺陷

来源:互联网 发布:数据挖掘的研究现状 编辑:程序博客网 时间:2024/05/16 04:56

gprof是一个程序性能分析工具,通过监测程序运行,返回函数动态调用关系、函数调用次数以及每个函数的执行时间,从而有利于程序员发现性能瓶颈,对程序进行优化。对gprof的使用介绍,网上已经很多,例如百度百科上的介绍(wikipedia上没有专门介绍gprof的页面),在此不多赘言。这两天看了gprof作者当年发表的介绍其实现的论文,简单说一下其原理和缺陷。

在gprof出现之前,Unix系统中已经有类似的工具prof,记录每个函数的执行次数和时间。prof的缺点是没有记录函数调用关系,例如A函数调用了B函数,A执行了10次,用时1毫秒,B执行了20次,用时2毫秒,在prof中返回的数据就是A执行10次1毫秒,B执行20次2毫秒,没有把B的执行时间加入到A中,导致在实际应用中,各个函数的执行时间都差不多,很难发现瓶颈。另一个问题是,如果C函数也调用了B函数并且B执行了100次,那么就不知道这100次中A和C分别调用了多少次。

gprof对prof的主要改进就是加入对函数动态调用关系的分析和记录,将子函数的执行时间加入到父函数中。例如在以上例子中,gprof返回的结果将是A用时3毫秒B用时2毫秒。概括起来说,gprof在被评测程序的每个函数运行前插入评测程序,记录以下三类程序运行数据:

1. 函数运行次数。

2. 函数执行时间。在分时操作系统中,用函数开始时间和结束时间的差作为执行时间不准确,因为这段时间内该函数并不独占CPU. 为了解决这个问题,prof和gprof都采用了采样的方法,即每隔一段时间就对程序计数器(PC)进行采样,根据多少个采样点落入该函数的PC范围来估算实际执行时间。

3. 函数调用关系。函数调用关系包括动态调用关系和静态调用关系,前者是运行时决定,后者是由源代码决定的。gprof主要使用动态调用关系,辅以静态关系。在取得了动态函数调用关系图之后,在分析函数运行时间时,将子函数的运行时间加入到父函数中。

简单介绍了其原理后,再说一下gprof的主要缺陷:

1. 函数执行时间是估计值。如前所说,函数执行时间是通过采样估算的。这个不是什么大的问题,一般估算值与实际值相差不大,何况任何测量都不可能100%准确。

2. gprof假设一个函数的每次执行时间是相同的。这个假设在实际中可能并不成立,例如,如果函数B执行100次,总运行时间时间10毫秒,被A调用20次,被C调用80次,那么B的10毫秒中有2毫秒加入到A的执行时间,8毫秒加入到C的执行时间中。实际上,很可能B被A调用时的每次执行时间和被C调用时的每次执行时间相差很大,所以以上分摊并不准确,但gprof无法做出区分。

3. 不适合存在大量递归调用的程序。如果存在递归调用时,函数动态调用关系图中将存在有向环,这样明显不能将子函数的运行时间加到其父函数中,否则环将导致这个累加过程无限循环下去。gprof对此的解决办法是用强连通分量(strongly-connected components)将这些递归调用的函数在调用关系图中坍缩成一个节点来处理,但在显示最终结果时仍然分别显示各个函数的运行时间。缺点是,对于这些递归调用的函数,其执行时间不包括其子函数的执行时间,如prof一样。所以当程序中存在大量递归调用时,gprof退化为老的prof工具。

4. 不能处理内联函数。由于gprof只记载函数调用,如果程序员用大量内联函数的话,将不能被gprof发现。

5. 数据表示不直观。gprof将结果输出到二维的终端中,因此对于树状结构的表示不够直观。当然这不是大问题,习惯了就行。

对于2和3,后来出现的程序性能评测工具有改进方案,那就是不仅记录程序调用关系图,而且记录程序调用栈。这样做的话增加了工具的运行负荷,因此需要降低采样频率来保证工具的性能。

原创粉丝点击