转载请保留以下声明
  作者:赵宗晟
  出处:https://www.cnblogs.com/zhao-zongsheng/p/13067733.html

很多软件都要做性能分析和性能优化。很多语言都会有他的性能分析工具,例如如果优化C++的性能,我们可以用Visual Studio自带的性能探测器,或者使用Intel VTune Profiler。了解性能分析工具的原理有助于了解工具给出的数据与结果,也能帮助我们在遇到异常结果时排查哪里出了问题。这篇博客简单总结一下常见的性能分析工具原理。

性能分析器原理分类

性能分析工具大部分都可以分为下面几类

  1. 基于采样(Sampling)
  2. 基于插装(Instrumentation)
  3. 基于事件(Event-based)

1. 基于采样

基于采样的分析器会每隔一个固定时间间隔暂停所有线程的执行,然后分析有哪些线程正在执行,那些线程处于就绪状态。对于这类线程,我们记录每个线程的调用堆栈,以及其他需要的信息。我们称这个行为为一次采样,记录下来的每个堆栈为一个样本。然后在结束性能分析的时候我们统计记录下载的堆栈,看每个堆栈被记录了多少次,或者每个函数被记录了多少次。统计学告诉我们,那些执行时间比较长的函数、堆栈,被记录的次数会更多。如果堆栈A被记录了200次,堆栈B被记录了100次,那么堆栈B的执行时间是堆栈A的2倍。我们可以计算某个堆栈样本的数量占总样本数的比例,这个比例就是堆栈执行时间的占比。用Visual Studio的性能探测器我们看到的百分比和数字就是值样本的占比(也就是时间占比)和样本次数。

很多性能分析工具都是基于采样的方式。运行性能分析器是会影响被测程序的性能的,而基于采样的有点是对性能影响比较小,不需要重新编译程序,非常方便。

2.基于插装

插装是指通过修改程序,往里面加入性能分析相关代码,来收集、监控相关性能指标。例如我们可以在一个函数的开头写下计数代码,我们就可以知道在运行中这个函数被执行了多少次。一般来说基于插装的性能分析更为准确,但是对性能影响比较大。因为需要修改代码,所以也不是很方便。另外,基于插装的分析也可能会引起海森堡bug(heisenbug)。海森堡bug是指当你再运行程序的时候能遇到这个bug,但是试图定位这个bug时却遇不到这个bug。这个bug往往是因为在定位bug时修改了软件的运行环境/流程,导致软件执行和生产时不一样,于是就遇不到这个bug了。这个命名的来源很有意思,海森堡是量子力学的著名科学家,他提出了不确定性原理,以及观察者理论。这个理论认为,观察会影响例子的状态,导致观察粒子和不观察粒子会导致不同的结果,这个和海森堡bug的情形非常相似。关于观察者理论,有兴趣的人可以再了解一下。

回到正题,基于插装也可以再进行划分:

  • 人手修改源码:这个是我们非常常用的性能分析方法。我们做性能分析有时候就会直接修改源码,计算某一段代码的执行时长,或者计算函数调用次数,分析哪段代码耗时。
  • 工具自动修改源码
  • 工具自动修改汇编/中间码
  • 运行时注入
  • ......

3.基于事件

在软件执行过程中,可能会抛出某些事件。我们通过统计事件出现的次数,事件出现的时机,可以得到软件的某些性能指标。事件又可以分为软件事件和硬件事件。软件事件比如Java可以在class-load的时候抛出事件。硬件事件则是使用专门的性能分析硬件。现在很多CPU里面都有用于分析性能的性能监控单元(PMU),PMU本身是一个寄存器,在某个事件发生时寄存器里面的值会+1。例如我们可以设置为当运行中发生memory cache miss的时候PMU寄存器+1,这样我们就知道一段时间内发生了多少次memory cache miss。性能分析器使用PMU时,会给PMU设置需要统计的事件类型,以及Sample After Value (SAV)。SAV是指寄存器达到什么值之后出发硬件中断。例如,我们可以给PMU设置SAV为2000,统计事件为memory cache miss。那么当cache miss发生2000次时,发生硬件中断。这个时候性能分析器就可以收集CPU的IP,调用堆栈,进程ID等等信息,分析器结束时进行统计分析。

基于硬件事件的优点是,对被测程序的影响非常小,比基于采样还小,可以使用更短的时间间隔收集信息,而且可以收集CPU的非常重要的性能指标,例如cache miss,分支预测错误,等等。

但是基于硬件事件的分析器也有它的一些问题,导致数据上的误差:

  • SAV一般会设置成很大的数值:
    像是Intel VTune Profiler一般会把SAV设置成10,000到2,000,000,发生中断时我们能知道最后一次触发该事件是哪段代码引起的,但是在这之前的9,999到1,999,999次事件我们是不知道的。他会认为所有10,000到2,000,000次事件都是由同一处代码引起的。如果发生了很多次中断,收集了很多次信息,而某一行代码出现了很多次,那么根据统计学,我们能知道这行代码触发了多少次事件。但是如果某一行代码只出现了一两次,那么我们很难知道这行代码到底出发了多少次时间。
  • CPU一个核只有几个PMU,但是可以统计的事件有几十种:
    一个PMU可以统计一个事件,但是我们分析一个软件可能需要统计几十种事件。一般的处理方法是多路复用(Multiplexing)。比如说前10ms记录事件A,后10ms记录事件B,再后10ms由记录事件A,……,这样轮流记录事件A和事件B,那么一个PMU就可以同时统计两个事件。多路复用可以解决少量PMU统计大量事件的问题,但是也会导致每种事件记录的样本数减少,倒是最后统计计算的结果误差更大。
  • Intel® Hyper-Threading Technology导致记录不准:
    Hyper-Threading技术可以让一个CPU物理核变成两个逻辑核,但是这两个逻辑核会共享很多硬件,包括PMU。这会出现什么问题呢?例如我们有两个线程再两个CPU核同时运行。我们觉得实在两个核上执行,但是实际上是在同一个物理核上。所以PMU会同时统计两个程序触发的事件,我们很难区分到底是哪个逻辑核出发了多少事件,所以PMU记录的数据就会不准确。另外,性能分析器计算性能指标时会使用一些硬件参数,Hyper-Threading技术会让这些参数不准确。例如一般个CPU核能再一个clock执行4个uop,所以CPI(Cycle Per Instruction,每个clock执行的指令数)是0.25。但是如果使用了Hyper-Threading技术,实际的CPI会是0.5
  • Event Skid(事件打滑)会导致记录的IP不准确:
    PMU记录有些事件会出现一定延时,所以在执行分析器的中断处理代码时,可能被测程序已经又执行了好多指令,IP已经向后滑动了很远了。一般如果我们只是统计函数的话不会太大问题,但是我们想统计指令时就会有很大问题了。比如我们可能会看到某个add指令导致了大量的分支预测错误,显然这个是不可能的。往往这种时候我们可以看看前面一点的指令。
  • Interrupt Masking(中断屏蔽),导致统计出来的空闲状态比正常的高
    不同的中断有不同的优先级,为了高优先级的中断处理程序不被打断,我们可以选择屏蔽一部分中断事件。但是PMU的事件也是一个中断,如果系统中有大量的中断屏蔽,那么会有PMU的中断被屏蔽掉一部分,导致统计出来的数据不准确。表现出来的效果就是,统计出来的处于空闲状态的时间比实际的要高。

总结

这几个就是比较常见的性能分析方法。我们知道了性能分析的原理,可以更好地理解性能分析器给出的结果,也可以在出现明显异常的结果时,分析判断可能的原因,针对性的解决。

参考

https://en.wikipedia.org/wiki/Profiling_(computer_programming)
https://software.intel.com/content/www/us/en/develop/articles/understanding-how-general-exploration-works-in-intel-vtune-amplifier-xe.html
https://software.intel.com/content/www/us/en/develop/documentation/vtune-help/top/analyze-performance/user-mode-sampling-and-tracing-collection.html
https://software.intel.com/content/www/us/en/develop/documentation/vtune-help/top/analyze-performance/hardware-event-based-sampling-collection.html

CPU性能分析工具原理的更多相关文章

  1. Linux性能优化:CPU性能分析工具--vmstat

    Blog:博客园 个人 目录 参数说明 输出信息说明 procs memory swap io system cpu 示例 vmstat是Virtual Meomory Statistics(虚拟内存 ...

  2. CPU性能分析

    CPU性能分析工具 lscpu:查看CPU硬件信息 lscpu Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Litt ...

  3. Linux下性能分析工具汇总

    来自:http://os.51cto.com/art/201104/253114.htm 本文讲述的是:CPU性能分析工具.Memory性能分析工具.I/O性能分析工具.Network性能分析工具. ...

  4. 系统级性能分析工具 — Perf

    从2.6.31内核开始,linux内核自带了一个性能分析工具perf,能够进行函数级与指令级的热点查找. perf Performance analysis tools for Linux. Perf ...

  5. Linux性能分析工具与图形化方法

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~. 作者:赵坤|腾讯魔王工作室后台开发工程师 在项目开发中,经常会遇到程序启动时间过长.CPU使用率过高等问题,这个时候需要依靠性能分析工具来 ...

  6. 系统级性能分析工具perf的介绍与使用[转]

    测试环境:Ubuntu16.04(在VMWare虚拟机使用perf top存在无法显示问题) Kernel:3.13.0-32 系统级性能优化通常包括两个阶段:性能剖析(performance pro ...

  7. Linux 性能分析工具汇总合集

    出于对Linux操作系统的兴趣,以及对底层知识的强烈欲望,因此整理了这篇文章.本文也可以作为检验基础知识的指标,另外文章涵盖了一个系统的方方面面.如果没有完善的计算机系统知识,网络知识和操作系统知识, ...

  8. [转]Linux性能分析工具汇总合集

    出于对Linux操作系统的兴趣,以及对底层知识的强烈欲望,因此整理了这篇文章.本文也可以作为检验基础知识的指标,另外文章涵盖了一个系统的方方面面.如果没有完善的计算机系统知识,网络知识和操作系统知识, ...

  9. 超全整理!Linux性能分析工具汇总合集

    转自:http://rdc.hundsun.com/portal/article/731.html?ref=myread 出于对Linux操作系统的兴趣,以及对底层知识的强烈欲望,因此整理了这篇文章. ...

随机推荐

  1. view组件的封装是否需要特有模型?

    必须需要. 现在接手的老项目,所有自定义组件全部使用的原始的全量数据,作为模型给view用来展示. 结果发现,基本数据的选择错误,需要选择另一个数据作为基本数据,这导致一个很麻烦的问题,需要改动全部的 ...

  2. ExtJS动态隐藏Panel中按钮

    1.直接隐藏 在bbar的按钮中直接加属性:hidden : true 属性,可隐藏:disabled : true 属性,可禁用 在columns列中直接加属性:hidden : true 属性,可 ...

  3. 获取cookie里面的sessionid

    做接口的时候我们需要获取一个令牌,类似于token以及sessionid这样的签名,也可以说是一个令牌. import urllib.request, urllib.parse, urllib.err ...

  4. Nginx 的过滤模块是干啥用的?

    上一篇文章我写了 Nginx 的 11 个阶段,很多人都说太长了.这是出于文章完整性的考虑的,11 个阶段嘛,一次性说完就完事了.今天这篇文章比较短,看完没问题. 过滤模块的位置 之前我们介绍了 Ng ...

  5. 基于Pytest豆瓣自动化测试【1】

    -- Pytest基础使用教程[1] 引言 Pytest 是一个非常实用的自动化测试框架,目前来说资料也是非常多了.最近某友人在学习 Python的一些测试技术,帮其网上搜了下教程:发现大多数文章多是 ...

  6. 01.Django-基础

    基础 1. 简介 Django是一个由Python写成的开放源代码的重量级Web应用框架. Django的目的是使常见的Web开发任务,快速和容易 2. 特点 MVC开发模式 内置进行快速web开发所 ...

  7. Java之预定义

    作为Java初学者的我,提供一个类似C#的预处理机制.若有不足之处,敬请各位大佬指正(感觉没有,哈哈哈哈哈哈)! Java 没有类似 C++的宏,也没有类似C#的预定义 #if...#endif C# ...

  8. MVC案例之模糊查询与删除

    查询操作: Servlet         //1. 调用 CustomerDAO 的 getAll() 得到 Customer 的集合 List<Customer> customers ...

  9. ArrayList及List的常用方法

    ArrayList package com.aff.coll; import java.util.ArrayList; import java.util.List; import org.junit. ...

  10. day 7 while循环

    #隐式布尔值: 0 none 空# 一.流程控制# 短路运算:偷懒原则,偷懒到哪个位置,就把当前位置的值返回# 为0# (10>3 and 10 and 0 and 10 )or( 10> ...