Linux perf使用思考

一、参考资料(建议阅读)

  (1)程序员性能之道,从使用perf开始!
  (2)手把手教你系统级性能分析工具perf的介绍与使用(超详细)
  (3)perf优化cache实例

二、值得思考的几个问题

1、perf使用不同的性能事件进行统计有什么区别呢?

  在使用 perf 进行性能分析时,可以选择不同的性能事件进行统计,每个事件都提供了不同类型的性能信息。选择不同的性能事件可以帮助你重点关注特定的性能方面,以下是一些常见的性能事件及其区别的示例:

  1)CPU周期数(CPU cycles):这个事件跟踪了 CPU 执行指令的周期数,可以用来评估程序的执行效率和指令的优化情况。比较 CPU 周期数可以帮助你发现某个程序瓶颈在于执行时间较长的代码段。

  2)指令数(Instructions):这个事件跟踪了执行的指令数量,可以帮助你评估程序的指令级别的性能。指令数高的代码段可能是 CPU 密集型的部分。

  3)缓存相关事件(Cache-related events):包括缓存命中率、缓存失效等相关事件。缓存相关事件可以帮助你了解程序对缓存的使用情况,如缓存的命中效率、缓存失效引起的开销,从而针对性地优化程序的内存访问模式。

  4)分支预测失误(Branch mispredictions):这个事件跟踪了分支预测错误的次数,分支预测错误会导致流水线中断和指令重排,从而影响程序的性能。

  5) 缺页中断(Page faults):这个事件跟踪了缺页中断的次数,缺页中断通常表示内存访问受限,可以用来评估程序的内存访问模式和资源利用情况。

  选择不同的性能事件可以让你聚焦于特定的性能方面,以便更好地发现问题和进行优化。通过观察不同性能事件的统计数据,你可以了解程序在各个方面的状况,并找到需要改进的地方。根据具体的应用场景,选择适合的性能事件能够更精确地分析和优化程序的性能表现。

2、那使用不同的性能事件统计出来的数据?排序是如何决定的,其中的百分比数值在不同的性能事件进行统计时各自的意义又是什么?

  在 perf 工具中,数据的分析和排序是根据所选择的性能事件和相关指标来进行的,不同的性能事件和指标可以提供不同的视角和信息。以下是一些常见的方法和概念,用于分析 perf 收集的性能数据:

  1)排序方式:perf 默认使用性能事件的计数值来排序,即按照事件发生的频率或数量进行排序。你可以通过 -s--sort 参数来改变排序方式,例如 -s cpu 表示按照 CPU 占用排序,-s dso 表示按照动态共享对象排序。

  2) DSO(Dynamic Shared Object):perf 默认按照动态共享对象来分组和排序,动态共享对象是程序在运行时使用的共享库。通过对 DSO 进行排序,可以看到各个共享库对性能的贡献,帮助你定位问题并进行优化。

  3)百分比数值:perf 输出中的百分比数值通常表示该事件在总体中的占比。例如,如果一个函数的 CPU 使用时间占总 CPU 使用时间的 10%,则会显示为 10.00%。你可以根据百分比数值来确定相对重要性,找到有影响的代码段和函数。

  4)热点函数/代码段:通过观察排序后的性能数据,你可以找到热点函数或代码段,即最耗费时间或资源的部分。这些热点可以成为性能优化的关注点,针对性地进行修改和优化。

  5) 调用图(Call graph):perf 还提供了生成函数调用图的功能,它显示了函数之间的调用关系和调用路径。调用图可以帮助你理解性能问题的根源,确定函数的调用路径和调用频率,以及找到有潜在优化空间的函数。

  分析 perf 收集的性能数据需要结合具体的应用场景和问题。你可以着重关注具有高占比和频率的性能事件,找到引起性能问题的原因,并根据结果进行性能优化和改进。同时,你还可以通过调用图、堆栈信息等进一步深入分析性能问题。记住,性能分析是一个迭代的过程,需要反复优化和验证,以改善系统的性能与效率。

3、perf的 计数模式 与采样模式

  计数模式采样模式可以使用相同的性能事件,但它们所提供的数据统计和分析方式是不同的。

  在计数模式下,您可以指定要计数的特定性能事件,例如指令的执行数、缓存命中数、分支预测错误等。perf会实时监测这些事件的发生次数,并提供相应的计数结果。这样可以对事件的频率进行精确的计量,得到准确的计数信息。

  而在采样模式下,您同样可以选择相同的性能事件来进行采样,以收集数据。采样会在程序执行过程中间隔一定的时间或事件触发时进行,记录事件发生时的上下文信息,如函数调用堆栈、指令指针等。采样会提供更详细的数据,可以用于分析热点函数、调用关系、性能瓶颈等。

  虽然计数模式和采样模式可以监测和记录相同的性能事件,但计数模式提供了精确的事件计数,而采样模式提供了更丰富的上下文信息。因此,在选择模式时,您需要根据具体的分析需求考虑所需的数据类型和精度。

  总结起来,计数模式和采样模式可以使用相同的性能事件,但它们所提供的数据类型和分析方式是不同的,具体取决于您的分析目标和准确度需求。

三、CPU部分性能指标解释

1、EXC_RETURN:异常返回次数

  在性能分析中,异常返回(Exception returns)是指程序中发生异常并从异常处理程序返回到正常执行流程的次数。当程序执行过程中遇到异常,例如除零错误段错误等,系统会检测到异常并跳转到相应的异常处理程序进行处理。当异常处理程序完成后,程序会从异常处理程序返回到原来的执行流程,继续执行。

  异常返回可以是由于程序中的异常处理逻辑导致的,也可以是由于外部事件(如系统资源不足、信号等)触发的。异常返回次数的计数器可以帮助我们了解程序中异常的频率和规模,以及异常处理代码的性能影响。

  异常返回的频率较高可能会对程序的性能产生不利影响,因为异常会导致程序的正常执行流程中断,并且处理异常通常涉及额外的开销。因此,分析异常返回次数可以帮助我们确定异常处理代码的效率和优化的方向,以提高程序的性能和稳定性。

  请注意,异常返回次数是 perf 工具中提供的一种性能事件,你可以使用 perf 进行性能分析,收集和分析异常返回的数据,从而了解程序中异常的情况和对性能的影响。

2、EXC_TAKEN:发生的异常次数

  在性能分析中,EXC_TAKEN事件指的是程序中发生的异常的次数。异常是在程序执行过程中遇到的意外情况,可以是由于错误的操作、无效的数据或其他不可预测的情况而引发的。

  异常可以分为不同类型,常见的异常类型包括但不限于以下几种:

1. 除零错误(Divide by Zero):使用除法操作时除数为零引发的异常。

2. 空指针异常(Null Pointer Exception):当代码试图通过空引用访问对象的成员时引发的异常。

3. 内存访问异常(Segmentation Fault):试图访问无效地址或越界的内存区域导致的异常。

4. 数据类型异常:包括类型转换错误、越界访问数组等数据类型相关的异常。

5. 文件访问异常:如文件不存在、权限不足等导致无法正常读写文件的异常。

  异常是破坏程序正常执行流程的事件,应该被正确地处理,以避免程序崩溃或产生其他不可预测的结果。异常的发生次数可以通过 EXC_TAKEN 事件来统计和分析,帮助我们了解异常的频率和规模,进而对异常情况进行调试和优化。

3、branch-misses

  branch-misses 事件计数器用于统计处理器分支预测(branch prediction)错误(branch misprediction)的次数。当处理器在执行分支指令时,会根据预测结果提前执行分支的某个方向的指令,以提高指令流水线的效率。但是,在某些情况下,处理器的分支预测会出现错误,导致执行了错误的分支方向。这就是分支预测错误(branch misprediction)。

  要优化程序代码以减少分支预测错误(branch-misses)的发生,可以考虑以下几个方面:

  分支预测友好的编码风格:尽量使用顺序执行的代码结构,避免复杂的条件分支和嵌套。简化程序流程可以减少分支预测错误的机会。

  避免频繁的分支跳转:过多的分支跳转会增加分支预测错误的几率。可以通过重构代码,减少不必要的分支跳转,例如合并相邻的条件判断,展开循环等。

  提高代码局部性:尽量保持代码的局部性,减少分支发生的频率和深度。可以通过适当的循环展开、代码重排等技术来优化代码的局部性。

  使用逻辑运算替代条件分支:在某些情况下,可以使用逻辑运算符(如 &&、||)代替条件分支语句,以减少分支预测错误的机会。

  减小分支预测错误的影响:分支预测错误的影响主要体现在指令流水线的停顿。可以通过优化指令调度、减少依赖关系等手段来降低分支预测错误对程序性能的影响。

  使用程序注解和优化指令:某些编程语言和编译器提供了特殊的注解或优化指令,用于提供分支预测的相关信息。可以在适当的地方使用这些特性,帮助编译器更好地进行优化和预测。

  利用硬件分支预测特性:现代处理器通常具备硬件分支预测机制,并提供了针对分支预测错误的优化技术。可以考虑利用特定的处理器指令或优化策略来最大程度地利用硬件分支预测。

  需要注意的是,分支预测错误的优化是一个复杂而细微的过程,具体的优化策略可能因程序的特征和目标平台的不同而异。建议使用性能分析工具(如perf)来分析程序的热点代码和分支预测错误的情况,并根据具体情况进行针对性的优化。

4、如何减少程序执行过程中出现的缺页异常?

  要减少程序执行过程中的缺页异常(Page Faults),可以采取以下几个措施:

  内存访问模式优化:缺页异常通常是由于内存访问模式不合理导致的,例如频繁地访问不在内存中的页。通过优化内存访问模式,尽量避免频繁地访问不在内存中的页,可以减少缺页异常的发生。

  数据局部性优化:数据局部性是指程序对数据的访问呈现出一定的空间和时间上的局部性规律。通过合理地组织数据结构和算法,尽量利用好数据的局部性,可以提高内存访问的效率,减少缺页异常的发生。

  预读取(Prefetching):预读取技术可以在程序访问某个数据之前,提前将其加载到缓存或内存中。预读取可以减少缺页异常的发生,提高数据的可用性和访问的效率。可以使用系统级别的预读取策略,也可以在代码中手动进行预读取。

  内存分配优化:合理管理内存分配可以减少缺页异常的发生。例如,避免过度的内存分配和释放,减少频繁的内存碎片化,可以提高内存的利用率。

  使用大页面(Large Pages):使用大页面可以减少缺页异常的次数。大页面是指比传统的页面更大的内存区域,通过减少页表的数量,从而降低了缺页异常的发生频率。请注意,使用大页面可能需要系统级别的支持,并且需要根据特定的应用场景进行评估和配置。

  需要根据具体的程序和系统环境来选择和实施这些优化措施。通过综合考虑程序的访问模式、数据局部性、内存分配、预读取等因素,以及实际测试和性能分析的结果,可以有效地减少缺页异常的发生,提高程序的性能和响应速度。

  欢迎大家指导和交流!如果我有任何错误或遗漏,请立即指正,我愿意学习改进。期待与大家一起进步!

猜你喜欢

转载自blog.csdn.net/weixin_45842280/article/details/132797623