【工具】使用perf抓取火焰图

embedded/2024/10/19 18:41:49/

背景

当程序存在cpu性能问题时,我们需要找到是哪个函数占用较多的CPU,也就是找出热点函数;perf的火焰图就是这个用途

安装

在Linux系统中,perf 是 Linux 内核提供的性能分析工具,它通常包含在内核源代码包中。大多数现代的Linux发行版都提供了预编译的 perf 包,可以直接通过系统的包管理器安装。

根据你所使用的Linux发行版,以下是一些不同发行版上安装 perf 的指令:

对于基于Debian的系统(比如Ubuntu):

sudo apt-get update
sudo apt-get install linux-tools-common linux-tools-generic linux-tools-`uname -r`

请注意,linux-tools-generic 包可能会安装不与你当前运行的内核版本完全匹配的 perf 版本。为了确保版本对应,使用 uname -r 来获取并安装与当前运行内核版本对应的 perf 版本。

对于基于Red Hat的系统(比如Fedora和CentOS):

对于 Fedora 你可以使用:

sudo dnf install perf

对于CentOS或者RHEL,可以使用:

sudo yum install perf

对于Arch Linux:

sudo pacman -S linux-tools

在安装后,你可以通过键入 perf --version 来检查 perf 是否成功安装以及其版本信息。

如果你需要更高级的功能,或者你的系统没有预编译的 perf 可用,你也可以选择从Linux内核源代码编译 perf 工具。这个过程较为复杂,需要下载相应版本的Linux内核源代码,并按照源代码中的说明来编译 perf。

样例代码

设计如下的程序:有两个线程,分别运行func1->func1_1(初始化大小为10的vector,0-9)、func1_2(初始化大小为100的vector,0-99); func2->func2_1(初始化大小为1000的vector,0-999)、func2_2(初始化大小为10000的vector,0-9999)

#include <iostream>
#include <thread>
#include <vector>void func1_1() {std::vector<int> v(10);int i = 0;for(auto e : v) {e = i++;}
}void func1_2() {std::vector<int> v(100);int i = 0;for(auto e : v) {e = i++;}
}void func2_1() {std::vector<int> v(1000);int i = 0;for(auto e : v) {e = i++;}
}void func2_2() {std::vector<int> v(10000);int i = 0;for(auto e : v) {e = i++;}
}void func1() {while(true) {func1_1();func1_2();}
}void func2() {while(true) {func2_1();func2_2();}
}int main() {std::thread t1(&func1);std::thread t2(&func2);if(t1.joinable()) {t1.join();}if(t2.joinable()) {t2.join();} return 0;
}

编译

g++ perf_demo.cpp -o perf_demo -lpthread

采集perf数据

  1. 找到要采集程序的pid
  2. 使用perf record命令来收集程序运行时的性能数据
    perf record -F 99 -p 12519 -g -- sleep 30
    
    参数解释如下:
    • -F 99:指定采样频率(99 次/秒),可以根据需要调整该值。
    • -p:要采集的进程pid。
    • -g:记录调用栈信息,这对于生成火焰图是必需的。
    • – sleep 30:收集性能数据的持续时间,这里设置为30秒。sleep 30 表示在收集数据后暂停30秒,以确保 perf 有足够的时间来记录信息。
  3. 然后会在当前目录创建一个名 perf.data的文件,其中包含了采样数据
  4. 生成火焰图
    • 下载安装FlameGraph工具,它是一组脚本,用来从 perf 的采样数据生成可视化的火焰图。
      安装方式:

      git clone https://github.com/brendangregg/FlameGraph.git
      
    • 按照前面提到的步骤,首先安装并使用 FlameGraph 工具转换采样数据

      perf script | /path/to/FlameGraph/stackcollapse-perf.pl > out.perf-folded
      
    • 使用转换后的数据生成SVG火焰图

      /path/to/FlameGraph/flamegraph.pl out.perf-folded > perf_demo.svg
      

      在这里插入图片描述

火焰图分析

火焰图提供了程序执行过程中的调用栈样本的可视化表示,它以一种直观的方式展示了哪些函数消耗了最多的CPU时间。下面是如何分析火焰图的一些基本步骤和技巧:

  1. 理解火焰图布局
  • 水平方向:显示了采样到的栈帧。每个横向的条块代表一个函数调用,其宽度代表该函数在采样中占据的相对时间比例。宽度越大,说明函数占用的CPU时间越长。
  • 垂直方向:展示了调用栈的深度。栈底部(图的最底层)通常是主函数或线程的起始点,更上层则代表函数的调用关系(被调用者在调用者之上)。
  1. 分析热点(Hot Spots)
    找到最宽的几个柱形区域,这些通常是性能的热点,即你的程序花费时间最多的地方。有时候,一个宽柱形可能是因为单个函数调用自身就很耗时,有时则是由于该函数被频繁调用。
  2. 检查调用路径
    从任意一个宽柱形开始,向上追踪它的调用路径。这可以帮助你理解为什么一个函数会变成热点。可能是它自身操作复杂,或者它被一个循环结构频繁调用。
  3. 注意递归调用
    如果你看到在火焰图中有重复的模式,这可能表示递归调用。递归调用可能是性能问题的来源,特别是当递归没有正确优化时。
  4. 考虑不同层级的影响
    尽量避免只关注顶部的函数。如果一个底层函数(位于火焰图较低位置)相对较宽,它可能被多个不同的上层函数调用,改进这样一个底层函数可能对整个程序性能有显著的提升。
  5. 比较前后变化
    如果你在进行性能优化工作,应该在更改代码后重新生成火焰图,然后将新旧火焰图进行比较。这将帮助你直观地看到更改对性能的影响。
  6. 关注宽度变化
    如果你注意到某个函数的子调用中有明显的宽度变化,这可能表示性能问题。它可能是计算密集型操作,或者存在条件判断导致的执行路径变化。

根据上面样例抓取的火焰图进行分析:

  1. func1(50%) 和 func2(50%) 宽度各占一半 – 两个线程,分别占使用cpu的一半
  2. func1_1(7%)、func1_2(36%)、func2_1(4%)、func2_2(39%)
  3. 可以得出来,热点函数是func1_2、func2_2

perf的原理

perf 是 Linux 内核提供的性能分析工具,可以用来进行系统级和进程级的性能分析。它使用了Linux内核中的性能计数器子系统 perf_events。以下是 perf 工具的基本工作原理:

  1. 性能计数器(Hardware Performance Counters)
    现代的CPU通常都带有硬件性能计数器,这些计数器可以在不影响系统性能的情况下,跟踪处理器内部事件,如指令执行次数、缓存命中/未命中次数等。

  2. perf_events 内核子系统
    perf 基于Linux内核中的 perf_events 子系统,后者提供了一种统一的接口来访问CPU硬件计数器以及其他监控事件,比如软件事件(上下文切换、页面缺失)和追踪点(tracepoints)。perf_events 子系统支持各种架构,并且是可扩展的。

  3. 事件采样(Sampling)
    通过 perf record 命令时,perf 会配置 perf_events 开始收集数据。通常是基于事件采样的方式进行的,即按照一定频率记录特定事件发生的时候的系统状态。当硬件计数器达到预设的阈值(采样频率),就会生成一个中断,perf 便在这个时候捕获当前的程序计数器(PC)、调用栈和其他寄存器的值等信息。

  4. 记录和分析
    采样得到的数据被写入到一个文件中(默认为 perf.data)。perf record 命令完成后,可以使用 perf report 查看和分析性能数据,或者使用 perf script 将数据导出供其他工具(如 FlameGraph)进一步分析。

  5. 调用栈捕获
    当启用 -g 选项时,perf 不仅会记录下触发采样的那一刻的程序计数器位置,还会尝试捕获当前的调用栈。这对于理解程序行为至关重要,尤其是识别哪些函数调用导致了大量的CPU使用。

  6. CPU和内核支持
    由于 perf 直接与CPU硬件和内核模块交互,它需要CPU支持特定的性能计数器特性,同时也需要内核支持 perf_events 接口。因此,旧的CPU或者特定的内核编译配置可能无法使用 perf 的某些功能。

综合以上,perf 的原理是通过硬件性能计数器和 perf_events 内核子系统,提供了一种方法来测量和分析系统运行时的详细性能指标,从而使开发者能够深入理解系统的瓶颈和性能问题所在。

一些思考

可以参考perf的设计,对自己的模块设计性能采集和分析工具,profiling工具。


http://www.ppmy.cn/embedded/128807.html

相关文章

【优选算法】(第三十八篇)

目录 数据流中的第K⼤元素&#xff08;easy&#xff09; 题目解析 讲解算法原理 编写代码 前K个⾼频单词&#xff08;medium&#xff09; 题目解析 讲解算法原理 编写代码 数据流中的第K⼤元素&#xff08;easy&#xff09; 题目解析 1.题目链接&#xff1a;. - 力扣&…

嵌入式Linux:发送实时信号

目录 1、发送进程 2、接收进程 非实时信号有一个明显的局限性&#xff1a;当同一个信号多次发生时&#xff0c;它只会被记录为一次&#xff0c;且不会记录发生的次数。因此&#xff0c;当该信号被解除阻塞后&#xff0c;它仅会被处理一次。这种行为使得标准信号在某些应用场景…

如果用Java设计MySQL中表级锁、行级锁和间歇锁会是怎么的?

在 MySQL 中&#xff0c;锁机制是确保数据一致性和并发控制的重要手段。MySQL 支持多种锁类型&#xff0c;包括表级锁、行级锁等&#xff0c;每种锁的适用场景、影响范围和实现机制各不相同。我们将逐一介绍它们&#xff0c;并通过模拟代码展示不同锁的实现。 1. 锁类型及其影…

文心智能体:我的旅游小助手

文章目录 一、全球旅游推荐官&#xff08;旅游小帮手介绍&#xff09;二、为什么会创建全球旅游推荐官呢&#xff1f;1.创意灵感2.实现思路 三、开发步骤和方法四、调试方法和总结五、探索AI未来&#xff0c;开启无限可能&#xff1a;文心智能体平台&#xff0c;智能创新的领航…

PHP $ _FILES [‘userfile‘] [‘name‘ ] 和 $ _FILES [‘userfile‘] [‘tmp_name‘] 有什么区别

在PHP中&#xff0c;当你通过HTML表单上传文件时&#xff0c;PHP会将与上传文件相关的所有信息存储在全局数组$_FILES中。这个数组是一个多维数组&#xff0c;其中包含了关于每个上传文件的详细信息。$_FILES[userfile]是这个多维数组中的一个元素&#xff0c;它代表了名为user…

《OpenCV计算机视觉》——人脸检测__Haar特征、级联分类器

文章目录 Haar特征一、定义与原理二、分类三、计算方法四、应用五、优缺点 级联分类器一、定义与原理二、结构与组成三、举例说明 Haar特征 Haar特征是一种在计算机视觉和图像处理中常用的特征描述方法&#xff0c;特别适用于物体识别&#xff0c;尤其是人脸检测。以下是对Haa…

HDFS开启审计日志

文章目录 HDFS开启审计日志修改 HDFS log4j.properties修改 HDFS hdfs-site.xml修改 HDFS hadoop-env.sh分发配置到NN节点重启NN节点评估 HDFS 审计日志大小 HDFS开启审计日志 修改 HDFS log4j.properties 修改文件大小及保留个数、日志存储目录 vim /opt/apache/hadoop/etc…

openlayers 测量功能实现(测距测面)- vue3

一、配置openlayer环境 借鉴&#xff1a;Vue 3 OpenLayers 的简单使用_vue3 openlayers-CSDN博客 二、代码如下&#xff08;测距、测面和清除&#xff09; measurs.js: import {ref} from vue; import Draw from ol/interaction/Draw import VectorSource from ol/source/…