性能分析

软件工程中,性能分析performance analysis也称为profiling),是以收集进程运行时信息为手段研究进程行为的分析方法,是一种动态程序分析的方法。

性能分析量测像是程序的空间或时间复杂度特定指令的使用情形、函数调用的频率及运行时间等。性能分析的目的在于决定进程的哪个部分应该被优化,从而提高进程的速度或者内存使用效率。

性能分析可以由程序的源代码或是可运行档进行。一般会使用称为性能分析工具(profiler)的工具进行。性能分析工具会使用许多不同的技术,可能是以事件为基础(Event-based)的、统计的、指令导向的、仿真的方法。性能分析工具常用在性能工程的过程中。

性能分析工具

CodeAnalyst性能分析工具的图像输出

"若要了解程序行为,程序分析工具非常重要。电脑架构分析师需要这类工具来评估程序在新的系统结构中运作的情形。软件撰写者需要这类工具来分析程序,并分析出其中关键的区块。编译器撰写者需要这类工具来评估其指令调度分支预测算法运作的情形" -- (ATOM, PLDI, '94)

性能分析工具使用广泛的技术手段收集数据,包括硬件中断代码指令作业系统hooking、CPU内置的性能计数寄存器,等等。

性能分析输出会是:

  • 观察到的事件的统计摘要(概要文档)
摘要配置文档信息通常会根据事件发生的源代码语句进行注释显示,因此测量数据的大小与进程的代码大小成线性关系。
/* ------------ 源代码------------------------- 发行次数 */            
0001             IF X = "A"                     0055
0002                THEN DO                      
0003                  ADD 1 to XCOUNT           0032
0004                ELSE
0005             IF X = "B"                     0055


  • 所有事件的记录流(亦称踪迹,英文“trace”)
事件的记录流则与指令路径长度成线性关系,也就是和运行时长成线性关系。由于数据量过大,有时记录会不切实际。所以,有些程序分析工具可以设置在某特殊条件下才启动事件踪迹的记录,在另外特殊条件下结束事件踪迹的记录。
对于顺序执行的进程,通常轮廓就足够了。但并行计算进程的性能问题(等待消息或者同步问题)和事件的时间顺序有关,因此需要全部的踪迹才能找到问题。
  • hypervisor持续性的交互监控(针对事件连续性或周期性显示在屏幕上)
在观看在运行程序的相关度量时.可在任意时刻启动或结束事件踪迹的记录,也可以在一些关键的点上暂停异步的程序来看和其他平行处理程序之间的交互关系。

历史

早在1970年代,IBM System/360IBM System/370的平台就有性能分析工具,一般是用计时器中断在固定的时间纪录程序状态字(PSW)来侦测程序运行时的「过热点」(hot spots)。这是早期使用抽样方式进行性能分析的范例之一。在1974年时,指令集仿真器就允许完整的事件踪迹,以及其他性能监控的机能。

以性能分析工具为主的UNIX程序分析至少可以回溯到1979年,当时Unix系统有一个基础工具prof,可以列出每一个函数,也列出此函数总共花了多少时间。1982年时gprof工具延伸此概念,可以列出完整的函数调用图[1]

1994年时,迪吉多的Amitabh Srivastava和Alan Eustace提出了描述ATOM的论文[2]。ATOM是一个平台,可以将程序配合其性能分析工具调整,在编译期间,ATOM会在要分析的程序中加入代码,而加入的代码会输出分析数据,这种修改程序,输出自身分析数据的技术,称为逻辑注入

2004年时,gprof和ATOM论文都出现在前50个最具影响力的编程语言设计和实现会议(PLDI)论文中[3]


以输出方式分类

一般性能分析器

一般性能分析器(flat profiler)根据函数调用计算平均的函数调用次数,而且不会根据被调用函数或是运行脉络(context)细分函数调用次数。

函数调用图性能分析器

函数调用图性能分析器(call graph profiler)[4]会显示函数被调用的次数及频率,也会列出函数调用链(call-chains),有些软件会列完整的调用链,有些不会。

输入敏感性能分析器

输入敏感性能分析器(input-sensitive profiler)[5][6][7]将性能度量与输入工作负载特征(例如输入大小或输入值)相关联,相比于一般性能分析器或调用图分析器增加了一个维度。它会生成图表描述应用进程性能随其输入的变化情况。

以分析方式的分类

性能分析器本身也是程序,可以在被分析程序运行时收集相关信息,来分析该程序。根据收集到信息的细微度,以及收集信息的方式,可以分为事件为基础的性能分析器,或是统计式的性能分析器。有些性能分析器为了收集信息,会中断程序的运行,因此在时间量测上有一定的分辨率限制。

事件为基础的性能分析器

以下列出的编程语言有事件为基础的性能分析器:

  • JavaJVMTI(JVM工具接口)API,以前称为JVMPI(JVM性能分析接口),提供给性能分析器的hook,可以抓到像函数调用、类别加载、卸载、线程的进入及离开等事件。
  • .NET框架:利用性能分析的API,可以连接到像是COM服务器的性能分析代理器(profiling agent)。像Java一様,在运行会提供许多回调函数给代理器,可以捕捉到像是方法JIT/进入/离开,对象创建及其他。特别的是性能分析代理器可以用任意方式改写目的应用程序的字节码。
  • Python:Python的性能分析包括profile模块,以调用函数图为基础的hotshot,以及用'sys.setprofile'函数来捕捉像c_{call,return,exception}及python_{call,return,exception}的事件。
  • Ruby:Ruby也用类似Python的性能分析界面。目前有在profile.rb中的一般性能分析器及相关模块。

统计式的性能分析器

有些性能分析器是用取样的方式运作。取様式的性能分析器利用操作系统中断,在固定时间取様目的程序的调用栈。取様式的性能分析器在数值上较不精准,但对目的程序运行时间的影响最小,允许目的程序可以在接近全速的速度下运作。

所得到的数据不是精准值,只是统计上的近似值而已。「实际误差的量一般会大于一个采样时间。若某一数值是取様时间的n倍,其误差约为n倍取样时间的平方根。」[8]

在实务上统计式的性能分析器会比其他的分析方式更能知道目的程序各部份占的比例,而且相较之下有较少的边际效应(例如内存缓存或是指令解码的管道线等),由于统计式的性能分析器对程序运行速度的影响较小。因此可以侦测到一些其他方式侦测不到的问题。这种方式可以看出用户模式及可中断系统模式(例如系统调用)分别占的时间。

不过由于系统程序需处理中断,仍然会花一些CPU的运行周期,分散缓存的读取,而且无法分辨在不可中断内核模式下的行为。

有些特制的硬件可以克服这类的问题:有些最近MIPS微理器中,JTAG接口有一个PCSAMPLE寄存器,可以用一种无法侦测到的方式来取様程序计数器。

最常用的统计式的性能分析器包括AMDCodeAnalyst苹果公司Shark(OSX)、oprofile(Linux)、IntelVTune及Parallel Amplifier(Intel Parallel Studio的一部份)。

插装型的性能分析器

有些性能分析可以用插装(也称为逻辑注入)的方式处理目的程序,也就是在目的程序中加入额外指令来收集需要的信息。

程序的插装会影响程序的性能,可能会出现不精确的结果及 heisenbug(捉摸不定,不易重现的bug)。插装一定会对程序运行有些影响,常见的情形是使程序变慢。不过插装可以特定只针对部份程序,而且可以小心控制以使影响降到最低。其对于特定程序的影响是看插装放置的位置,以及捕捉踪迹(trace)的机制。有些处理器有硬件支持可以捕捉踪迹,插装可以只占一个机器语言周期的时间。一般可以从结果中移除插装的影响。

gprof是一个同时用插装及取様的性能分析器的例子。插装用来取得被调用函数的信息,而实际花的时间则是由取様方式来获得。

插装是决定性能分析器可控制程度及时间分辨率的关键。以下是一些方式的分类。

  • 手动:是由编程者加入指令,在运行时计算相关信息,例如计算事件或是调用像是应用程序响应测试(ARM)标准的API
  • 源代码层级自动处理:依照插装政策,利用自动化工具自动在源代码中加入instrumentation,像Parasoft公司的Insure++
  • 中间语言:在汇编语言或是bytecode中加入针对多种高端语言的instrumentation,,例如OpenPATOpenPAT
  • 编译器协助:像gprof和Quantify都是这类的例子,像用gcc -pg ...可以使用gprof,用quantify g++ ...可以使用Quantify。
  • 二进位翻译:此工具在编译好的可运行档中加入instrumentation,例如ATOM。
  • 运行时插装:代码直接在运行前修改,工具可以完成的监控及控制程序的运行,例如用PinValgrindDynamoRIO
  • 运行时注入:修改程度比运行时插装要小,代码在运行时修改,令加入跳跃到协助用函数的指令,例如和DynInst

解释器式的插装

  • 解释器式调试选项,可以在解释器处理每个目的指令时,收集性能量度的相关信息。像字节码控制表JIT的解释器都在目标代码运行时有完整的控制能力,因此有机会收集到非常全面的数据。

hypervisor/模拟器(simulator)

  • Hypervisor:在hypervisor下运行(一般而言)没有修改的程序,可以收集相关信息,例如SIMMON工具。
  • 模拟器hypervisor:在指令集仿真器运行(一般而言)没有修改的程序,可以交互式及选择性的收集相关信息,例如SIMONOLIVER工具。

相关条目

  • 算法效率
  • 静态程序分析
  • 评效基准
  • 性能分析工具列表
  • 性能工程
  • 性能预测
  • PAPI是一套针对现代微处理器的硬件性能计数器的可携式接口(以函数库的方式提供)
  • 性能调校
  • 最坏条件运行时间(WCET)
  • Java性能
  • 软件考古学

参考数据

  1. gprof: a Call Graph Execution Profiler 页面存档备份,存于 // Proceedings of the SIGPLAN '82 Symposium on Compiler Construction, SIGPLAN Notices, Vol. 17, No 6, pp. 120-126; doi:10.1145/800230.806987
  2. Amitabh Srivastava and Alan Eustace, "Atom: A system for building customized program analysis tools", 1994 (download 页面存档备份,存于) // Proceeding PLDI '94 Proceedings of the ACM SIGPLAN 1994 conference on Programming language design and implementation. Pages 196 - 205, doi:10.1145/773473.178260
  3. . [2013-12-14]. (原始内容存档于2017-10-18).
  4. Graham, Susan L.; Kessler, Peter B.; Mckusick, Marshall K. . Proceedings of the 1982 SIGPLAN symposium on Compiler construction - SIGPLAN '82 (Boston, Massachusetts, United States: ACM Press). 1982: 120–126. ISBN 978-0-89791-074-3. doi:10.1145/800230.806987 (英语).
  5. Coppa, Emilio; Demetrescu, Camil; Finocchi, Irene. . IEEE Transactions on Software Engineering. 2014-12-01, 40 (12): 1185–1205 [2022-02-18]. ISSN 0098-5589. doi:10.1109/TSE.2014.2339825. (原始内容存档于2022-02-18).
  6. Zaparanuks, Dmitrijs; Hauswirth, Matthias. . Proceedings of the 33rd ACM SIGPLAN Conference on Programming Language Design and Implementation (Beijing China: ACM). 2012-06-11: 67–76 [2022-02-18]. ISBN 978-1-4503-1205-9. doi:10.1145/2254064.2254074. (原始内容存档于2022-02-18) (英语).
  7. Lin, Hai-Xiang (编). . Lecture Notes in Computer Science 6043. Berlin, Heidelberg: Springer Berlin Heidelberg https://link.springer.com/10.1007/978-3-642-14122-5. 2010. ISBN 978-3-642-14121-8. doi:10.1007/978-3-642-14122-5 (英语). 缺少或|title=为空 (帮助)
  8. Statistical Inaccuracy of gprof Output 存盘,存档日期2012-05-29.

外部链接

This article is issued from Wikipedia. The text is licensed under Creative Commons - Attribution - Sharealike. Additional terms may apply for the media files.