【发布时间】:2010-10-02 10:06:08
【问题描述】:
当我在做一个大学项目时,我使用了一个由一位年长学生制作的项目内部分析器,它非常基本但足够好,因为它的任务是减去代码两点之间的时间并提供统计信息。
现在,专业的分析器是如何工作的?它是否预处理代码以插入检查点或类似的东西?它是否读取带有调试数据的二进制代码来捕获函数的调用位置?
谢谢。
【问题讨论】:
标签: profiler
当我在做一个大学项目时,我使用了一个由一位年长学生制作的项目内部分析器,它非常基本但足够好,因为它的任务是减去代码两点之间的时间并提供统计信息。
现在,专业的分析器是如何工作的?它是否预处理代码以插入检查点或类似的东西?它是否读取带有调试数据的二进制代码来捕获函数的调用位置?
谢谢。
【问题讨论】:
标签: profiler
这取决于所分析的代码类型,例如 .NET CLR 为代码分析器提供 facility。在处理托管代码时,可以重写中间代码以注入自定义挂钩。您还可以分析应用程序的堆栈跟踪。操作系统可以提供分析方法,例如 Windows 有performance counters。在处理嵌入式代码时,您可以模拟/替换底层硬件以有效监控系统性能。
【讨论】:
有两种常见的分析策略(无论如何对于基于 VM 的语言):检测和采样。
Instrumentation 插入检查点并在每次方法启动和完成时通知分析器。这可以通过 JIT/解释器或通过后正常编译但预执行阶段完成,该阶段仅更改可执行文件。这会对性能产生非常显着的影响(从而扭曲任何时序结果)。不过,这有助于获得准确的计数。
Sampling 会定期向 VM 询问所有线程的堆栈跟踪是什么样的,并以这种方式更新其统计信息。这通常对性能的影响较小,但产生的调用计数不太准确。
【讨论】:
对于*nix 中的gprof,在编译和链接时使用-pg,一些额外的代码被注入到目标代码中。然后通过运行gprof,注入代码生成报告文件。
【讨论】:
有许多不同的分析器以不同的方式工作。
常用的分析器只是定期检查正在运行的程序,以查看当前正在执行的汇编指令(程序计数器)以及哪些例程调用了当前函数(调用堆栈)。这种采样分析器可以使用标准二进制文件,但如果您有调试符号来计算程序中给定地址的代码行,则更有用。
除了定期采样外,您还可以使用处理器性能计数器在一定数量的事件(例如缓存未命中)后进行采样,这将帮助您了解程序的哪些部分由于内存访问而变慢。
其他分析器涉及重新编译程序以插入指令(称为instrumentation)以计算每组连续指令(基本块)执行的频率,或者甚至记录基本块的顺序被执行,或者在某些地方记录变量的内容。
检测方法可以为您提供您可能想要的所有精度和数据,但会减慢程序的速度并改变其性能特征。相比之下,使用基于采样的方法,您可以根据您获得的配置文件数据的准确性,根据运行程序所需的时间长度调整性能影响。
【讨论】:
正如 Jon Skeet 上面所写的,有两种策略:检测和采样。
仪器既可以手动完成,也可以自动完成。在手动情况下:开发人员手动插入代码以跟踪感兴趣代码区域的开始/结束。例如一个简单的“StartTimer”和“EndTimer”。一些分析器工具也可以自动执行此操作 - 为此,分析器需要对代码进行静态分析,即解析代码并识别重要的检查点,例如特定方法的开始/结束。这对于支持反射的语言(例如任何 .net 语言)来说是最容易的。使用“反射”,分析器能够重建整个源代码树(以及调用图)。
采样由分析器完成,它会查看二进制代码。分析器还可以使用 Hooks 或捕获 Windows 事件/消息等技术来进行分析。
检测和采样方法都有自己的开销。开销的数量取决于 - 例如如果将采样频率设置为较高的值,则分析本身会对报告的性能产生重大影响。
仪器与采样: 这并不像一种方法比另一种方法更好。两者都有自己的位置。
最好的方法是从基于采样的分析器开始,然后查看整个系统级别。即运行采样器并查看系统范围的资源使用情况:内存、硬盘、网络、CPU。
从上面确定正在阻塞的资源。
利用上述信息,您现在可以在代码中添加检测以查明罪魁祸首。例如,如果内存是最常用的资源,那么它将有助于检测与内存分配相关的代码。请注意,通过检测,您实际上是专注于代码的特定区域。
【讨论】: