分析程序执行
分析器是一个可以生成另外一个程序的执行时间的统计结果的程序。分析器可以输出一份
包含每个语句或函数的执行频度、每个函数的累积执行时间的报表。
许多编译器套件,如 Windows 上的 Visual Studio 和 Linux 上的 GCC 都带有分析器,可以
帮助我们找到程序中的热点。微软曾经只在价格昂贵的 Visual Studio 版本中提供了分析
器,但是自 Visual Studio 2015 社区版开始,微软开始向开发者提供免费的分析器。当然,
在 Windows 上还有其他开源的分析器以及对应早期的 Visual Studio 版本的分析器。
有几种方式可以实现一个分析器。一种可以同时支持 Windows 和 Linux 的方法如下。
(1) 程序员设置一个特殊的可以分析程序中所有函数的编译选项,重新编译一次程序,让
程序变为可分析的状态。这涉及在每个函数的开始和结束处添加一些额外的汇编语言
指令。
(2) 程序员将可分析的程序链接到分析库上。
(3) 每次这个可分析的程序运行时都会在磁盘上生成一张分析表(profiling table)。
(4) 分析器读取分析表,然后生成一系列可阅读的文字或图形报告。
另外一种分析方法是这样的。
(5) 通过将优化前的程序链接至分析库上使其变为可分析状态。分析库中的例程会以非常高
的频率中断程序的执行,记录指令指针的值。
(6) 每次可分析的程序运行时都会在磁盘上生成一张分析表。
(7) 分析器读取分析表,然后生成一系列可阅读的文字或图形报告。
分析器的输出结果可能会有多种形式。一种形式是一份标记有每行代码的执行次数的源代
码清单。另一种形式是一份由函数名和该函数被调用的次数组成的清单。第三种形式同样
也是函数清单,不过里面记录的是每个函数的累计执行时间和在每个函数中进行的函数调
用。还有一种形式是一份函数和在每个函数中花费的总时间的清单,但不包括调用其他函
数的时间、调用系统代码的时间和等待事件的时间。
分析器的分析功能都是量身设计的,它自身的性能开销非常小,因此它对程序整体运行时
间的影响也很小。通常,程序中每个操作的执行速度只会被降低几个百分点。第一种方法
的分析结果会非常精确,代价是更高的间接成本和禁用了某些优化。第二种方法的测量结
果是近似值,而且可能会遗漏一些非频繁地被调用的函数,但是它的优点是可以直接运行
于正式产品之上。
分析器的最大优点是它直接显示出了代码中最热点的函数。优化过程被简化为列出需要调查
的函数的清单,确认各个函数优化的可能性,修改代码,然后重新运行代码得到一份新的分
析结果。如此反复,直至没有特别热点的函数或是你无能为力了为止。由于分析结果中的热
点函数从定义上来说就是代码中发生大量计算的地方,因此,通常这个过程是直截了当的。
以我个人的分析经验来看,对调试构建(debug build)的分析结果和对正式构建(release
build)的分析结果是一样的。在某种意义上,调试构建更易于分析,因为其中包含所有的
函数,包括内联函数,而正式构建则会隐藏这些被频繁调用的内联函数。
使用分析器是一种帮助我们找到要优化的代码的非常好的方式,但也有它的问题。
• 分析器无法告诉你有更高效的算法可以解决当前的计算性能问题。去优化一个低效的算
法只是浪费时间。
• 对于会执行许多不同任务的待优化的程序,分析器无法给出明确的结果。例如,一个
SQL 数据库在执行 insert 语句时和在执行 select 语句时所运行的代码是不一样的。因
此,当使用 insert 加载数据库时的热点代码,可能在数据库执行 select 语句的时候完
全不会被运行。除非在分析时会进行大量计算,否则请在测试中混合加载数据库操作和
查询数据库操作,使执行 insert 语句的代码在分析结果中不那么突出。
因此,要想容易地找出最热点的函数,请尽量一次仅优化一个任务。这对于分析整个程
序中的一个子系统在测试套件上的运行情况非常有帮助。不过,如果每次只优化一个任
务,那么也会引入另外一种不确定性:即它不一定会改善程序的整体性能。而实际上当
程序运行多个任务时,优化的效果可能就体现得不那么明显了。
当遇到 IO 密集型程序或是多线程程序时,分析器的结果中可能会含有误导信息,因为
分析器减去了系统调用的时间和等待事件的时间。不计算这些时间在理论上是完全合理
的,因为程序并不需要为这些等待时间负责。但是结果却是分析器可以告诉我们程序做
了多少事情,而不是花了多少实际时间去做这些事情。有些分析器不仅统计了函数调用
的次数,还计算出了每个函数的调用时间。如果函数调用次数非常多,意味着分析器可
能隐藏了实际时间。
分析器并不完美。有些优化可能性可能不会被分析出来,而且程序员在理解分析器的输出
结果时也可能会有问题。不过,对于许多程序来说,分析器的分析结果已经足够好了,不
需要再使用其他的优化方法了。