第5章 性能分析方法

news2024/11/16 7:22:36

有时看到修改后程序的运行时间发生变化时,却不清楚具体原因是什么。单独的时间信息有时无法给出问题发生的根本原因。

程序运行时硬件和软件都可以采集性能数据,硬件是指运行程序的CPU,软件是指操作系统和所有可用于分析的工具。通常软件栈提供上层指标,比如时间、上下文切换次数和缺页次数,而CPU可观察缓存未命中、分支预测错误等。根据要解决问题的问题,各指标的重要程度不一样。性能分析工具Linux perf可同时使用来自操作系统和CPU的数据。

本章将介绍一些流行的性能分析方法,如代码插桩、追踪、表征和采样,也会讨论静态性能分析方法、以及不需运行实际应用程序的编译器优化报告。

5.1 代码插桩

代码插桩可能是第一个被发明的性能分析方法,它通过在程序中插入额外的代码来采集运行时信息。

基于插桩的剖析方法常被用在宏观层次,而不是微观层次。在优化大段代码的场景,使用该方法通常会给出很好的洞察结果,因为你可以自上而下(现在主函数插桩,然后再往被调函数插桩)地定位性能问题,对于不熟悉代码库的人来说特别有用。

优化对象不仅是代码、也是数据。例如,渲染太慢是因为没有压缩网格,物体运动太慢是因为场景中有太多对象。

插桩的缺点:
        1. 它并不能提供任何关于代码如何操作系统或CPU角度执行的信息。例如,进程调度执行的概率或者发生多少次分支预测错误的信息。
        2. 每当需要插桩新内容(比如一个变量)时,都需要重新编译。这可能成为工程师的负担,增加分析时间。
        3. 在热点代码插入代码可能会导致整个基准测试的速度降低为原来的1/2。
        4. 可能改变程序的行为,无法看到之前相同的现象。

手动插桩会增加实验之间的时间间隔,并消耗很多的开发时间。于是自动化插桩被广泛在编译器中使用。编译器自动对整个程序进行插桩,并收集和运行相关的统计信息,经典的有代码覆盖度检查和基于剖析文件的编译优化。

值得一提的是,二进制插桩是在已经构建的可执行文件上完成的,而不是在源代码上完成。二进制插桩有2种类型:静态插桩(提前完成)和动态插桩(在程序执行时按需插桩)。动态二进制插桩的主要优点是它不需要程序重新编译和链接。此外,使用动态插桩可以将插桩限制在感兴趣的代码区域,而不是用在整个程序。

Intel Pin在发生被关注事件时,Pin会拦截程序的执行,并从该点开始生成新的插桩代码,它可以收集各种运行时信息,例如:
        1. 指令计数和函数调用计数;
        2. 拦截应用程序中函数调用和指令的执行;
        3. 通过捕获程序区域开头的内存和硬件寄存器状态,可以实现程序区域的“记录和重放”;

5.2 跟踪

代码插桩假设开发者可以掌控程序的代码。然而,跟踪依赖于程序外部依赖项的现有插桩。Linux的strace是内核的插桩。Intel Process Traces是CPU的插桩。跟踪可以从预先插桩好并且不容易改变的组件中获得,所以跟踪通常用在黑盒场景,即用户不能修改应用程序代码,但是又想深入了解程序在幕后做了什么的场景。

跟踪的开销取决于要跟踪的目标,系统调用多,开销大,反之则小。为了弥补这个缺陷,跟踪工具提供了过滤功能,可以只采集指定时间段或指定代码段的数据。

与代码插桩类似,通常跟踪也是为了探究系统中的异常。例如你可能想知道10s的无响应的时间中应用程序发生了什么。

跟踪技术对于调试工作非常有用,它的基本特性支持基于已记录的踪迹来“记录和回复”使用场景。其中一个工具是Mozilla rr调试器。大多数跟踪工具都能够使用时间戳标记事件,这使我们能够将其与那段时间内发生的外部事件关联。

5.3负载表征

负载表征是通过量化参数和函数来描述负载的过程,它的目标是定义负载的行为及主要特征。大体而言,应用程序分类为:交互式应用、数据库、网络应用、并行式应用。

5.3.1 统计性能事件

PMC通常有2种使用模式:计数和采样。计数模式用于负载表征,而采样模式用于寻找热点。计数背后的思想是统计程序运行期间某些性能事件的数量。perf stat工具用来统计各种硬件事件,如指令数、时钟周期数、缓存未命中等。

5.3.2 手动收集性能计数

现代CPU有几百个可统计的性能事件,理解何时使用特定的PMC就更难了。我们不推荐使用手动收集特定的PMC的原因在此。更建议使用Intel VTune Profiler来自动处理。

可以通过perf list查看可用的映射名称列表。

基于CPU的性能剖析工具在虚拟环境或者云环境中还是不能很好的运行。

5.3.3 事件多路复用和缩放

在某些情况下,我们想统计许多不同的事件,但是一个计数器一次只能统计一个事件,这就是为什么PMU中有多个计数器。自顶向下分析TMA在一次程序执行中需要收集多达100个不同的性能事件。

如果事件多与计数器,则分析工具使用时间多路复用技术使每个事件都有机会访问监控硬件。此时一个事件不会一直被测量,只有它的一部分能被测量。计算公式如下:
                                                最终计数 = 原始计数 * (运行时间/启用时间)

多路复用技术和缩放技术可以安全地用于在长时间间隔内执行相同代码的稳定负载。如果程序经常在不同的热点之间跳转,就会出现盲点,在缩放过程中出现错误。为了避免缩放,可以尝试减少事件的数量使其不大于可用的物理PMC数量。

5.4 采样

采样能给出“代码中那个位置对某些性能事件的数量贡献最大?”的答案。

采样所收集的样本数据存储在数据收集文件中,这些可进一步用于显示调用图、程序中最耗时的部分和统计意义上重要的代码段控制流。

5.4.1 用户模式采样和基于硬件事件的采样

采样分为用户模式采样和基于硬件事件的采样EBS。用户采样是一种纯软件方法,它将代理库嵌入被分析的应用程序中。代理库为应用程序中每个线程设置OS计时器,在计时器计时完成时,应用程序会接收到SIGPROF信号,该信号由收集器处理。

用户模式采用只能识别热点,而EBS可涉及PMC的其它分析。EBS开销比用户模式小,收集频率更高,数据也更准确。

5.4.2 寻找热点

在准备工作(了解想采样内容)之后,PMC计数器每个时钟周期递增一次,最终它会溢出,然后硬件发起PMI。剖析工具会被配置成捕获PMI,一个中断服务例程来处理它们。

想知道热点列表中出现的每个函数中的热点代码段。要查看内联函数的剖析数据以及为特定源代码区域生成的汇编代码,需要在应用程序构建时带上调试信息-g。使用-gline-tables-only选项可以将采集的调试信息减少到源代码对应的符号行号。

5.4.3 采集调用栈

用Linux perf工具收集调用栈有3种方法:
        1.帧指针perf record--call-graph fp。需要二进制文件在编译构建时带上--fnoomit-frame-pointer参数。
        2. DWARF调试信息perf record--call-graph dwarf。需要二进制文件在编译构建时带上-g。
        3. Intel最后分支记录(last branch record, LBR)硬件特性perf record--call-graph lbr。

了解调用栈的采集机制非常重要。开发者应当使用剖析工具来完成这项工作,它们可以更快、更准确地提供数据。

5.4.4 火焰图

火焰图是一种常用的可视化剖析数据和程序中最热代码路径的方法。

5.5 屋顶线性能模型

屋顶线性能模型是一种面向吞吐量的性能模型,常用于HPC。屋顶线表示应用程序不可能超过计算机处理能力的事实,程序中每个函数或者循环都收到计算机的计算或内存能力限制。

提高应用程序性能的传统方法是充分利用计算机的SIMD核多核功能,通常在向量化、内存和线程方面进行优化。在屋顶线图中,可以绘制标量单核、SIMD单核和SIMD多核性能的理论最大值,了解理论提升空间。

对于计算密集型而言,向量化和多线程。对于访存密集型,优化访问模式。

使用屋顶线模型优化性能的最终目标是将点向上移动,向量化和线程化将点上移,而通过增加算术强度优化内存访问将点右上移。

确定硬件限制后,根据屋顶线模型评估应用程序的性能。自动收集屋顶线模型数据最常用的2种方法是采样和二进制插桩。采样收集数据的开销较低,而二进制插桩可以通过更准确的结果。

总之,屋顶线性能模型有助于:
        1. 识别性能瓶颈;
        2. 指导软件优化;
        3. 确定何时优化达到了极限;
        4. 评估与机器能力相关的性能。

5.6 静态性能分析

对于C++而言,我们有诸如Clang Static Analyzer、Klocwork和Cppcheck等工具,它们旨在检测代码的正确性和语义。静态性能分析器不运行实际代码而是模拟代码运行,不过预测精度很难保证。

        1. 不可能静态分析C++代码的性能,因为不知道它们被编译成什么机器码。因此,静态性能分析更适用于汇编代码。
        2. 静态分析工具是模拟而不是执行,过程会很慢。所以选取一些汇编代码片段阐释预测,静态性能分析应用范围很窄。

静态分析工具的优点在于不需要真正的硬件就可以模拟不同CPU代系的代码,无需担心结果的一致性。缺点在于通常无法预测和模拟现代CPU中所有内容。

动态工具证明性能假设的唯一100%可靠的方法。缺点在于:
        1. 需要访问特权,才能收集PMC之类底层性能数据;
        2. 编写好的基准测试程序并衡量想要的指标并不总是那么容易;
        3. 过滤噪声和各种副作用。

5.7 编译器优化报告

为了更好地与开发者进行交互,编译器提供了性能优化报告。如果我们想知道某个函数是否被内联,或者某个循环是否被向量化、展开?一种比较困难的分析办法是分析生成的汇编指令。但是,如果函数比较大时,这会特别困难阅读汇编指令。幸运的是,包括GCC\ICC和Clang在内的大多数编译器都提供了优化报告,供开发者检查编译器对特定代码片段做了那些优化。

编译器报告是按源文件生成的,输出的报告可能非常大。Complier Explorer网站有针对LLVM的编译器的“优化输出”工具,当鼠标悬停在代码行上,这些工具会报告执行过的转换。

编译器优化报告不仅有助于发现错过的优化机会,还可以解释发生这种情况的原因,而且对测试优化假设也很有用。编译器通常会根据其成本模型分析结果来确定某种转换是否有益,并通过#pragma、属性、编译器内建函数等提示编译器。

编译器优化报告应该成为你工具箱中关键工具之一,它能快速检查是否对特定热点代码进行了优化以及一些重要的优化是否失败了。

5.8 本章总结

        1.时延和吞吐量通常是衡量性能的最终指标。
        2. 代码插桩帮我们跟踪程序中许多内容,但有较大开销。
        3. 跟踪有助于探索系统中异常。跟踪捕获完整的事件序列,且每个事件都附有时间戳。
        4. 负载表征是一种根据运行时行为比较和分组应用程序的方法。
        5. 采样会跳过程序执行的大部分时间,只取一个假定可以代表整个采样间隔的样本,但是它仍然能给出足够精确的样本分布。不需要重新编译,开销小,所以它是非常流行的性能分析方法。
        6. 计数和采样会产生非常低的运行时开销(通常低于2%)。如果在不同事件之间进行多路复用,计数的开销会变得更高(5%~15%),而随着采样频率的增加,其开销也会更高。
        7. 屋顶线模型是一种面向吞吐量的性能模型,有助于识别性能瓶颈,指导软件优化和跟踪优化进度。
        8. 静态分析工具模拟代码片段。该方法有很多约束和限制,但是可以获得一份非常详细的底层分析报告。
        9. 编译器优化报告有助于找到错过的编译器优化点,可以指导开发者构建新的性能实验。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/900067.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

设计模式篇---抽象工厂(包含优化)

文章目录 概念结构实例优化 概念 抽象工厂:提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。 工厂方法是有一个类型的产品,也就是只有一个产品的抽象类或接口,而抽象工厂相对于工厂方法来说,是有…

qsort函数详解

大家好,我是苏貝,本篇博客带大家了解qsort函数,如果你觉得我写的不错的话,可以给我一个赞👍吗,感谢❤️ 文章目录 一. qsort函数参数详解1.数组首元素地址base2.数组的元素个数num和元素所占内存空间大小w…

Xxl-job安装部署以及SpringBoot集成Xxl-job使用

1、安装Xxl-job: 可以使用docker拉取镜像部署和源码编译两种方式,这里选择源码编译安装。 代码拉取地址: https://github.com/xuxueli/xxl-job/tree/2.1.2 官方开发文档: https://www.xuxueli.com/xxl-job/#%E3%80%8A%E5%88%…

uni-app的Vue.js实现微信小程序的紧急事件登记页面功能

主要功能实现 完成发生时间选择功能,用户可以通过日期选择器选择事件发生的时间。实现事件类型选择功能,用户可以通过下拉选择框选择事件的类型。添加子养殖场编号输入框,用户可以输入与事件相关的子养殖场编号。完成事件描述输入功能&#…

C++笔记之条件变量(Condition Variable)与cv.wait 和 cv.wait_for的使用

C笔记之条件变量(Condition Variable)与cv.wait 和 cv.wait_for的使用 参考博客:C笔记之各种sleep方法总结 code review! 文章目录 C笔记之条件变量(Condition Variable)与cv.wait 和 cv.wait_for的使用1.条件变量&…

msf和cs联动

cs设置外部监听器 在vps上执行 vim /etc/ssh/sshd_config AllowTcpForwarding yes GatewayPorts yes TCPKeepAlive yes PasswordAuthentication yes systemctl restart sshd.service 在kali上执行,进行端口转发 ssh -C -f -N -g -R 0.0.0.0:1234:192.168.1.30:…

STM32开关输入控制220V灯泡亮灭源代码(附带PROTEUSd电路图)

//main.c文件 /* USER CODE BEGIN Header */ /********************************************************************************* file : main.c* brief : Main program body************************************************************************…

525. 连续数组

525. 连续数组 原题链接:完成情况:解题思路:参考代码: 原题链接: 525. 连续数组 https://leetcode.cn/problems/contiguous-array/description/ 完成情况: 解题思路: 参考代码: …

奇舞周刊第503期:图解串一串 webpack 的历史和核心功能

记得点击文章末尾的“ 阅读原文 ”查看哟~ 下面先一起看下本期周刊 摘要 吧~ 奇舞推荐 ■ ■ ■ 图解串一串 webpack 的历史和核心功能 提到打包工具,可能你会首先想到 webpack。那没有 webpack 之前,都是怎么打包的呢?webpack 都有哪些功能&…

喜讯?宁德时代首个零碳工厂成功建立,碳中和“任重道远”

8月19日消息,据宁德时代消息,广东瑞庆时代新能源科技有限公司宣布获得国际认可机构SGS颁发的PAS2060碳中和认证证书,从而正式成为一家零碳工厂。这标志着宁德时代首个储能电池为主的零碳工厂成功建立,也是公司继四川时代宜宾工厂之…

编程练习(3)

一.选择题 第一题: 函数传参的两个变量都是传的地址,而数组名c本身就是地址,int型变量b需要使用&符号,因此答案为A 第二题: 本题考察const修饰指针变量,答案为A,B,C,D 第三题: 注意int 型变…

速通蓝桥杯嵌入式省一教程:(五)用按键和屏幕实现嵌入式交互系统

一个完整的嵌入式系统,包括任务执行部分和人机交互部分。在前四节中,我们已经讲解了LED、LCD和按键,用这三者就能够实现一个人机交互系统,也即搭建整个嵌入式系统的框架。在后续,只要将各个功能加入到这个交互系统中&a…

赏味不足:详细来聊下轻资产运作,我从不做重资产

来源:BV1F84y1g7u3 好 大家好,是这样子的 对吧,因为最近聊了也很多,然后我在过程当中也发现,很多人对于不管是创业还是做副业,还是说找一条多种路,就是说可能不是那么清晰,然后我在这…

使用Dockerfile部署java项目

1、移动java包到创建的目录下 2、编写Dockerfile文件 在同一目录下使用如下命令创建文件 touch Dockerfile 文件内容如下: #依赖的父镜像 FROM java:8 #作者 MAINTAINER maxurui #jar包添加到镜像中 ADD springboot3-0.0.1-SNAPSHOT.jar springboot3-0.0.1-SNAPSHO…

射频同轴线阻抗

射频同轴线阻抗 射频同轴线的阻抗与线的绝缘介质的介电常数有关,与线的屏蔽层半径与内部导线半径的比值有关: R 0 1 2 π μ ′ ϵ ′ ln ⁡ ( r 2 r 1 ) \begin{align} R_0\frac{1}{2\pi}\sqrt{\frac{\mu^{}}{\epsilon^{}}}\ln(\frac{r_2}{r_1}) \en…

图数据库_Neo4j中文版_Centos7.9安装Neo4j社区版3.5.9_基于jdk1.8---Neo4j图数据库工作笔记0012

由于我们在国内使用啊,具体还是要用中文版滴,找了好久这个neo4j,原来还是有中文版的, https://we-yun.com/doc/neo4j-chs/ 中文版下载地址在这里: 所有版本都在这里了,需要哪个自己去下载就可以了,要注意下载以后,参考: https://we-yun.com/blog/prod-56.html 在这个位置下载…

Linux系统--进程间通信

文章目录 什么是进程间通信匿名管道命名管道 system V共享内存 system V消息队列 信号量 一、什么是进程间通信 首先由于进程运行是具有独立性的,具有独立的页表,PCB和虚拟地址空间等,父子进程间数据互补干扰。这就让进程间通信难度加大。由…

C语言:字符函数和字符串函数

往期文章 C语言:初识C语言C语言:分支语句和循环语句C语言:函数C语言:数组C语言:操作符详解C语言:指针详解C语言:结构体C语言:数据的存储 目录 往期文章前言1. 函数介绍1.1 strlen1.…

最新觅知扶风视频解析计费系统源码V1.8.2 免授权优化版 附教程

最新觅知扶风视频解析计费系统源码V1.8.2 免授权优化版 附教程 之前有分享过 1.7.1 的扶风计费系统,那个版本很久了之前也一直没有更新,拿到源码之后进行优化,因历史版本的加载和原版的加载速度真的是慢的感人,因此从零开始进行去…