程序性能分析

TODO

火焰图

参考:

https://blog.csdn.net/gatieme/article/details/78885908

https://github.com/brendangregg/FlameGraph
https://github.com/brendangregg/FlameGraph/blob/master/example-dtrace-stacks.txt
http://www.brendangregg.com/flamegraphs.html

火焰图就是通过定时采样堆栈信息,生成“时间占比-堆栈层次”的图表,通过观察函数执行时间占比(越宽占用时间越长)、CPU使用程度(颜色越深吃的CPU资源越多)来调优程序性能,“平顶山”式的图形一般就是要优化的地方。

局限性:由于原理是定时采样,信息是不完整的,无法观察到调用过深的情况;只适合观察同步阻塞,而通过异步实现的延迟无法观察。

用perf捕捉堆栈信息

程序编译时应使用-Og -g

1
# perf record -F 99 -p  -g

简单看看采集到的东西,但数据一多就没法看了:

1
# perf report -n --stdio

用FlameGraph生成火焰图

安装:b

1
yay -S flamegraph

生成:

1
# perf script | stackcollapse-perf.pl | flamegraph.pl > process.svg

uftrace

能记录函数进出信息和对应时标,能很方便地分析函数执行时间、频率等。

入侵式的分析,所以相较于perf能拿到完整、准确的log,而不是简单采样看看哪个函数占总时间的多,但要注意它会影响时序。

自带一个文字UI;带有可选依赖能支持完全动态调试,无需重新编译源码插入-gp选项;丰富的筛选功能,十分用户友好。

快速上手:https://uftrace.github.io/slide

基本用法:

1
2
3
$ make -gp -g abc.c
$ uftrace record ./a.out
$ uftrace replay

常用选项:

1
2
3
4
# 可控制打印的项目,可用项可以去`https://github.com/namhyung/uftrace/blob/master/doc/uftrace-live.md`的FIELDS里找
-f time

# 过滤函数

gprof

官方文档:https://sourceware.org/binutils/docs-2.32/gprof/index.html

用gprof和oprofile来分析 linux程序的性能

via: https://www.cnblogs.com/StitchSun/articles/4286428.html

  • gprof是GNU工具之一,它在编译的时候在每个函数的出入口加入了profiling的代码,运行时统计程序在用户态的 执行信息,可以得到每个函数的调用次数,执行时间,调用关系等信息,简单易懂。适合于查找用户级程序的性能瓶颈,对于很多时间都在内核态执行的程 序,gprof不适合。
  • oprofile也是一个开源的profiling工具,它使用硬件调试寄存器来统计信息,进 行profiling的开销比较小,而且可以对内核进行profiling。它统计的信息非常的多,可以得到cache的缺失率,memory的访存信 息,分支预测错误率等等,这些信息gprof是得不到的,但是对于函数调用次数,它是不能够得到的。。

简单来说,gprof简单,适合于查找用户级程序的瓶颈,而oprofile稍显复杂,但是得到的信息更多,更适合调试系统软件。

gprof原理与缺陷

via: https://pli53.wordpress.com/2011/01/23/gprof%E5%8E%9F%E7%90%86%E4%B8%8E%E7%BC%BA%E9%99%B7/

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

Tutorial: Using GNU profiling gprof with ARM Cortex-M

via: https://mcuoneclipse.com/2015/08/23/tutorial-using-gnu-profiling-gprof-with-arm-cortex-m/

这篇文章描述了如何在交叉编译环境下使用buildutils里的gprof——半主机模式。

Windows的性能监控工具xperf

via:https://www.jianshu.com/p/2304c3059c28

Xperf是一款系统级工具,是产品套件Windows性能工具包(Windows Performance Toolkit,WPT)的一部分,而WPT则属于微软软件开发包(Microsoft Software Development Kit,SDK)。Xperf是Windows事件跟踪系统(Event Tracing for Windows,ETW)的补充。ETW则是服务器操作系统的一个内置组件,提供详细的系统性能和系统数据。补充:WPT包含三款工具(xperf、xperfview、GPU):Xperf:使用CMD命令行工作。包括性能监控开始,收集,和打开等。XperfView:这是为Xperf的采样文件可视化显示的工具,可以绘制各种资源使用曲线,并用任意方式组织数据,排序。

https://blogs.msdn.microsoft.com/ntdebugging/2008/04/03/windows-performance-toolkit-xperf/

http://windowsitpro.com/windows-server/q-where-can-i-download-xperf-and-xperfview-tools
http://msdn.microsoft.com/en-us/windowsserver/bb980924.aspx