由于linux perf是非常流行的性能分析工具,因此本书使用该工具。该工具在绝大部分linux发行版本中都可以使用,这使得他的用户覆盖范围很广,此外,该工具是开源的。用户可以通过它了解典型剖析工具内部运行机制。这对理解本书讲到的概念是非常有帮助的。因为GUI类型的工具intel 的VTune Profiler 都倾向于隐藏所有的复杂点。
5.1 代码插桩
代码插桩可能是第一个被发明的性能分析方法,它通过在程序中插入额外的代码来采集运行时信息。代码清单6展示了最简单的代码插桩例子,即在函数开头插入printf 语句以统计函数的调用次数。
int foo(int x) {
printf("foo is called");
}
基于插桩的剖析方法常被用在宏观层次,而不是在微观层次。在优化大段代码的场景,使用该方法给出很好的洞察结果,因为你可以自上而下的定位性能问题。 虽然代码插桩在小型程序中并不是很有帮助。
值得一提的是,代码插桩在具有许多不同组件的复杂系统中表现非常出色。采样方法去除了这部分有价值的信息,不支持检测异常行为。
例如,不能提供进程调度执行的频率或者发生了多少分支预测错误,具有与应用程序本身相同的特权,运行在用户空间。没有访问内核的权限。
在讨论插桩时,有必要讨论一下二进制插桩方法。二进制插桩背后的思想也类似,不过是在已经构建的可执行文件上完成的,而不是在源代码上完成。可以静态插桩和动态插桩。
二进制插桩在性能分析和调试中非常有用,Intel Pin是非常流行的二进制插桩工具之一,在发生被关注事件时,Pin会拦截程序的执行,并从该点开始生成新的插桩代码。它可以收集各种运行时信息,例如:
1 指令技术和函数调用计数
2 拦截应用程序中函数调用和指令的执行。
3 通过捕获程序区域开头的内存和硬件寄存器状态,可以实现程序区域的记录和重放。
与代码插桩类似,二进制插桩只允许插桩用户空间代码,并且插桩后程序运行速度可能会非常慢。
5.2 跟踪
跟踪依赖于程序外部依赖项的现有插桩。例如,strace工具可以跟踪系统调用,可以被认为是linux 内核的插桩,Intel 处理器跟踪Intel Processor Traces 可记录程序执行的指令,可以被认为是对CPU的插桩。跟踪可以从预先插桩好并且不容易改变的组件中获得,所以跟踪通常用在黑盒场景,即用户不能修改应用程序代码,但是又想深入了解程序在幕后做了什么的场景。
strace和ltrace 分别是获取系统函数调用和库函数调用的指令。
跟踪的开销取决于跟踪的目标,例如,如果跟踪的程序几乎从不进行系统掉用那么strace 的跟踪开销几乎为零。相反,如果跟踪的程序非常依赖系统调用,那么开销会非常大。性能可能会劣化,变为原本的1/100。此外,由于跟踪不会跳过任何样本,所以会产生很多数据。
于代码插桩相似,性能剖析并不是为了解决该问题而设计的,但是使用跟踪技术可以
看到时什么导致了程序无响应。使用Intel PT工具。
跟踪技术对于调试工作非常重要,一个工具时Mozilla rr调试器,可以记录并回放进程,支持反响单步调试等功能。
5.3 负载表征
负载表征时通过量化参数和函数 来描述负载的过程,目标是定义负载的行为及主要特征。大体来看,应用程序属于以下类型中的一种或者多种。交互式应用程序,数据库,网络应用程序,并行式应用程序等。可以使用不同的指标和参数来描述不同的负载,以判断负载属于哪种应用程序领域。
我们将聚焦自顶向下的微架构分析TMA方法,它试图通过将应用程序划分为4种特征中的一类,4种特征分别为前端绑定,后端绑定,退休和错误投机。TMA实用性能监视计数器PMC,来收集所需的信息并识别CPU微架构的低效使用情况。
5.3.1 统计性能事件
PMC是一种非常重要的底层性能分析工具。他们可以提供关于程序执行的独有信息。PMC通常有两种使用模式:计数和采样。计数模式用于负载表征,而采样模式用于负载表征,而采样模式用于寻找热点。
计数器设置为0
配置事件 关闭计数功能
开启计数功能 读取计数器的值
开始运行基准测试
图18 统计性能事件的过程
图18中概述的步骤大致代表了典型分析工具在统计性能事件时的步骤,该过程在perf stat工具中实现的,可以用来统计各种硬件事件。
perf stat的输出示例如下
perf stat -- ./a.exe
这些数据很有用,首先它可以让我们快速发现一些异常,如缓存未命中率高或者IPC很差,当你对代码进行了改进并想要验证性能是否有提高时,它可能会派上用场,查看这些数字可以帮助你确定是否保留代码修改。
5.3.2 手动收集性能计数
现在CPU有几百个可统计的性能事件,我们很难记住所有的事件及其对应的意思,理解何时使用特定的PMC就更难了。这就是我们通常不推荐手动收集特定的PMC。
所有Intel系列的CPU的性能事件参见,每个事件都用十六进制的Event和Umask 值编码。有时,性能事件也可以用额外的参数进行编码。如Cmask和Inv等,表4展示了为Intel Skylake 微架构编码两个性能事件的示例。