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

背景

当程序存在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.mzph.cn/diannao/56860.shtml

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

相关文章

编码方式知识整理【ASCII、Unicode和UTF-8】

编码方式 一、ASCII编码二、Unicode 编码三、UTF-8编码四、GB2312编码五、GBK编码 计算机中对数据的存储为二进制形式&#xff0c;但采用什么样的编码方式存储&#xff0c;效率更高。主要编码方式有 ASCII、Unicode、UTF-8等。 英文一般为1个字节&#xff0c;汉字一般为3个字节…

Linux 线程互斥

1.相关背景概念 临界资源&#xff1a;多线程执行流共享的资源就叫做临界资源 临界区&#xff1a;每个线程内部&#xff0c;访问临界资源的代码&#xff0c;就叫做临界区 #include <iostream> #include <pthread.h> #include <string> #include <vector…

mac安装jdk8

这里写自定义目录标题 一、下载JDK8二、安装JDK8三、配置环境变量3.1 找到JDK安装目录3.2 打开终端&#xff1a;3.3 输入如下配置&#xff1a;3.3 查看配置是否成功&#xff1a; 一、下载JDK8 oracle官网下载或从下面链接获取 https://download.csdn.net/download/qq_44107684…

【小沐学Golang】基于Go语言搭建静态文件服务器

文章目录 1、简介2、安装2.1 安装版2.2 压缩版 3、基本操作3.1 go run3.2 go build3.3 go install3.4 go env3.5 go module 4、文件服务器4.1 filebrowser4.2 gohttpserver4.3 goFile 5、FAQ5.1 go.mod 为空5.2 超时 结语 1、简介 https://golang.google.cn/ Go语言诞生于2007…

day02 -- docker

1.docker的介绍 Docker 是一个开源的应用容器引擎&#xff0c;基于 Go语言 并遵从 Apache2.0 协议开源。Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中&#xff0c;然后发布到任何流行的 Linux 机器上&#xff0c;也可以实现虚拟化。容器是完全使…

●day 35 动态规划part01

第九章 动态规划part01 动态规划的类别 理论基础 动态规划下五步曲&#xff1a; 1、确定dp数组&#xff08;dp table&#xff09;以及下标的含义 2、确定递推公式 3、dp数组如何初始化 4、确定遍历顺序 5、打印dp数组 代码随想录 斐波那契数 代码随想录 动态规划5部曲 cla…

高级语言源程序转换为可执行目标文件

将高级语言源程序转换为可执行目标文件的过程通常包括以下几个主要步骤&#xff1a; ​ 1. 预处理&#xff08;Preprocessing&#xff09;&#xff1a; 由谁完成预处理器&#xff08;cpp&#xff09;操作处理源代码中的预处理指令&#xff08;如宏定义、文件包含、条件编译等&…

Linux——动态卷的管理

确保已经设置了对应的动态卷的驱动&#xff08;provisioner 制备器&#xff09;基于动态驱动创建对应的存储类创建PVC &#xff08;PVC 将会自动根据大小、访问模式等创建PV&#xff09;Pod的spec 中通过volumes 和 volumemounts 来完成pvc 的绑定和pvc对应pv的挂载删除pod 不…

Linux网络编程(七)-TCP协议客户端及代码实现

1.TCP的客户端代码流程简述 这一章将为大家讲解Socket通信中客户端的实现过程&#xff0c;还是先上图&#xff0c;请大家了解客户端的步骤 可以看到&#xff0c;相比服务端&#xff0c;客户端的步骤简单的很多。事实上这种情况比较多&#xff0c;比如一个服务端会有多个客户端…

JMeter模拟并发请求

PostMan不是严格意义上的并发请求工具&#xff0c;实际是串行的&#xff0c;如果需要测试后台接口并发时程序的准确性&#xff0c;建议采用JMeter工具。 案例&#xff1a;JMeter设置20个并发卖票请求&#xff0c;查看后台是否存在超卖的情况 方式一&#xff1a;一共10张票&…

TrickMo 安卓银行木马新变种利用虚假锁屏窃取密码

近期&#xff0c;研究人员在野外发现了 TrickMo Android 银行木马的 40 个新变种&#xff0c;它们与 16 个下载器和 22 个不同的命令和控制&#xff08;C2&#xff09;基础设施相关联&#xff0c;具有旨在窃取 Android 密码的新功能。 Zimperium 和 Cleafy 均报道了此消息。 …

编写一个通用的i2c控制器驱动框架

往期内容 I2C子系统专栏&#xff1a; I2C&#xff08;IIC&#xff09;协议讲解-CSDN博客SMBus 协议详解-CSDN博客I2C相关结构体讲解:i2c_adapter、i2c_algorithm、i2c_msg-CSDN博客内核提供的通用I2C设备驱动I2c-dev.c分析&#xff1a;注册篇内核提供的通用I2C设备驱动I2C-dev.…

时空数据时序预测模型: HA、VAR、GBRT、GCN、DCRNN、FCCF、ST-MGCN

HA (Historical Average) HA (Historical Average&#xff0c;历史平均模型) 是一种基础的时间序列预测方法&#xff0c;通常用于预测具有周期性或季节性规律的数据。它通过计算历史上同一时间段的平均值来预测未来值&#xff0c;假设数据会遵循某种周期性的变化模式。以下是对…

智能家居的“眼睛”:计算机视觉如何让家更智能

引言 在不远的未来&#xff0c;当我们走进家门&#xff0c;灯光自动亮起&#xff0c;空调已经调至最舒适的温度&#xff0c;甚至音乐也播放着我们最喜欢的歌曲。 这一切&#xff0c;都得益于智能家居系统的发展。而在这个系统中&#xff0c;计算机视觉技术扮演着至关重要的角色…

SpringBoot车辆管理系统:构建与优化

4系统概要设计 4.1概述 本系统采用B/S结构(Browser/Server,浏览器/服务器结构)和基于Web服务两种模式&#xff0c;是一个适用于Internet环境下的模型结构。只要用户能连上Internet,便可以在任何时间、任何地点使用。系统工作原理图如图4-1所示&#xff1a; 图4-1系统工作原理…

群晖通过 Docker 安装 MySQL

1. 打开 Docker 应用&#xff0c;并在注册表搜索 MySQL 2. 下载 MySQL 镜像&#xff0c;并选择版本 3. 在 Docker 文件夹中创建 MySQL&#xff0c;并创建子文件夹 4. 设置权限 5. 选择 MySQL 映像运行&#xff0c;创建容器 6. 配置 MySQL 容器 6.1 使用高权限执行容器 6.2 启…

scrapy 爬虫学习之【中医药材】爬虫

本项目纯学习使用。 1 scrapy 代码 爬取逻辑非常简单&#xff0c;根据url来处理翻页&#xff0c;然后获取到详情页面的链接&#xff0c;再去爬取详情页面的内容即可&#xff0c;最终数据落地到excel中。 经测试&#xff0c;总计获取 11299条中医药材数据。 import pandas as…

idea 2023 配置 web service

前言 能在网上查到的资料,都是比较老的,搞了一上午才配置好了环境 因此记录一下,服务你我他 我的环境: java 1.8,Idea2023.1 配置web service 服务端 直接新建一个java新项目 下载插件 添加框架支持 启动项目 配置web service 客户端 新建项目,下载三个插件的步骤和上面服务…

JMeter之mqtt-jmeter 插件介绍

前言 mqtt-jmeter插件是JMeter中的一个第三方插件&#xff0c;用于支持MQTT&#xff08;Message Queuing Telemetry Transport&#xff09;协议的性能测试。MQTT是一种轻量级的发布/订阅消息传输协议&#xff0c;广泛应用于物联网和传感器网络中。 一、安装插件 mqtt-jmeter项目…

【Hive】6-Hive函数、运算符使用

Hive函数、运算符使用 Hive内置运算符 概述 整体上&#xff0c;Hive支持的运算符可以分为三大类&#xff1a;关系运算、算术运算、逻辑运算。 官方参考文档&#xff1a;https://cwiki.apache.org/confluence/display/Hive/LanguageManualUDF 也可以使用下述方式查看运算符的…