嵌入式驱动学习第二周——使用perf进行性能优化

前言

这篇博客来聊一聊如何使用perf进行性能优化。

嵌入式驱动学习专栏将详细记录博主学习驱动的详细过程,未来预计四个月将高强度更新本专栏,喜欢的可以关注本博主并订阅本专栏,一起讨论一起学习。现在关注就是老粉啦!

目录

  • 前言
  • [1. perf介绍](#1. perf介绍)
  • [2. 安装perf](#2. 安装perf)
  • [3. perf初探](#3. perf初探)
    • [3.1 perf list使用](#3.1 perf list使用)
    • [3.2 perf record使用](#3.2 perf record使用)
    • [3.3 perf stat使用](#3.3 perf stat使用)
    • [3.4 perf top使用](#3.4 perf top使用)
    • [3.5 perf diff使用](#3.5 perf diff使用)
    • [3.6 火焰图](#3.6 火焰图)
  • [4. 实际操作](#4. 实际操作)
    • [4.1 生成执行文件并放入设备运行](#4.1 生成执行文件并放入设备运行)
    • [4.2 生成perf.data文件](#4.2 生成perf.data文件)
    • [4.3 生成火焰图](#4.3 生成火焰图)
  • [5. 问题](#5. 问题)
  • 参考资料

1. perf介绍

perf可以在CPU Usage增高的节点上找到具体引起CPU增高的函数,之后就可以有针对的聚焦那个函数做分析。

举例来说,使用 Perf 可以计算每个时钟周期内的指令数,称为 IPC,IPC 偏低表明代码没有很好地利用 CPU。Perf 还可以对程序进行函数级别的采样,从而了解程序的性能瓶颈究竟在哪里等等。Perf 还可以替代 strace,可以添加动态内核 probe 点,还可以做 benchmark 衡量调度器的好坏。

2. 安装perf

详见博主的另一篇博客,里面记录了ubuntu下和嵌入式系统重perf的安装过程,按照流程来是可以正常安装成功的:perf的安装与迁移

3. perf初探

3.1 perf list使用

perf list可以列出所有的采样事件。

bash 复制代码
perf list

至此,我们了解到event都有哪些类型,即每行最后[]中的内容,最主要的是三个event,如下所示:

Hardware event: 由PMU(电源管理单元,具有许多与普通计算机相似的组间,包括固件和软件,存储器,CPU,输入/输出功能)产生,如L1缓存命中等,当想了解程序对硬件特性的使用情况时,就可以对这些事件采样。

Software event: 由内核产生的事件,如进程切换等

Tracepoints event: 由内核静态跟踪点所触发的事件,这些tracepoint用来判断程序运行期间内核的行为细节,如slab分配器的分配次数等

3.2 perf record使用

perf record命令可以用来采集数据,并把数据写入数据文件中,随后可以通过perf report命令对数据文件进行分析。

具体有什么用呢?举例来说明。比如说我们已经断定目标程序计算量较大,也许是因为有些代码写的不够精简。那么面对长长的代码文件,究竟哪几行代码需要进一步修改呢?这便需要使用 perf record 记录单个函数级别的统计信息,并使用 perf report 来显示统计结果(perf record表示记录到文件,perf top直接会显示到界面)。

perf record常用选项如下所示:

bash 复制代码
-e: 选择一个事件,可以是硬件事件也可以是软件事件
-a: 全系统范围的数据采集
-p: 指定一个进程的ID来采集特定进程的数据
-o: 指定要写入采集数据的数据文件
-g: 使能函数调用图功能
-C: 只采集某个CPU的数据

同时还可以使用grep来使用程序名监控程序

bash 复制代码
perf record -e event -g -p grep your_program

perf record常用选项如下所示:

bash 复制代码
-i: 导入的数据文件名称,默认为perf.data
-g: 生成的函数调用关系图
--short: 分类统计信息,如PID、COMM、CPU

在使用上,可以先用perf record指令保存信息到perf.data中,在用perf report指令输出record的结果。

bash 复制代码
perf record -a -p -F 99 -- sleep 10
perf report

最后的结果如图所示:

3.3 perf stat使用

当我们接到一个性能优化任务时,最好采用自顶向下的策略。先整体看看该程序运行时各种统计事件的汇总数据,再针对某些方向深入处理细节。

有些程序运行的慢是因为计算量太大,起多数时间在使用CPU进行计算,这类程序叫CPU-Bound型;而有些程序运行的慢是因为过多的I/O,这时其CPU利用率应该不高,这类程序叫I/O-Bound型。这二者之间的调优是不同的。

perf stat选项,可以在终端上执行命令时收集性能统计信息

该指令的选项如下所示:

bash 复制代码
-a: 显示所有CPU上的统计信息
-c: 显示指定CPU上的统计信息
-e: 指定要显示的事件
-p: 指定要显示的进程ID

我们在本地跑一个进程,这个程序是一直向终端打印hello world,我们使用ps aux查看进程

bash 复制代码
ps aux

可以看到我们刚刚运行的hello的线程号是86

然后我们利用perf stat可以查看进程的相应信息,输入后还需要输入ctrl+c杀死进程

bash 复制代码
perf stat -p 86
ctrl+c(键盘上按这两个键)
  • task-clock(msec) 是指程序运行期间占用了xx个的任务时钟周期,单位为毫秒,该值高就说明程序的多数时间花费在CPU计算上而非IO
  • context-switches是指程序运行期间发生了xx次上下文切换,记录了程序运行过程中发生了多少次进程切换,频繁的进程切换应该是避免的(进程与进程间,内核态与用户态之间)
  • cpu-migrations是指程序运行期间发生了xx次CPU迁移,即用户程序原本在一个CPU上运行,后来迁移到另一个CPU
  • page-faults是指程序发生了xx次页错误
  • cycles:消耗的处理器时钟数,一条机器指令可能需要多个cycles
  • Instructions:机器指令数目,表示执行了多少条指令,IPC平均为每个CPU时钟周期执行了多少条指令
  • branches: 遇到的分支指令数
  • branch-misses: 预测错误的分支指令数
  • 其他可以控制的譬如分治预测、cache命中等

3.4 perf top使用

该指令用于实时显示当前系统的性能统计信息。该命令主要用来观察整个系统当前的状态,比如可以通过查看该命令的输出来查看当前系统最耗时的内核函数或某个用户进程

bash 复制代码
perf top

该指令可以看出进程中哪个函数消耗资源比较多

第一列显示给定函数正使用的CPU百分比

第二列显示使用函数的程序或库的名称

第三列显示函数名称或符号,内核空间中执行的功能由[k]标识,用户空间中执行的功能则用[.]标识

此外 perf top 还有常见的选项,如下所示:

bash 复制代码
-e: 指定要分析的性能事件
-p: 仅分析目标进程
-k: 指定带符号表信息的内核映像路径
-K: 不显示内核或者内核模块的符号
-U: 不显示属于用户态程序的符号
-g:显示函数调用关系图

3.5 perf diff使用

当多次perf record后,当前路径会生成一个perf.dataperf.data.old文件,分别表示本次和上次的record记录,如果要看二者之间的区别,对比优化结果,那么可以使用perf diff指令

bash 复制代码
sudo perf diff perf.data perf.data.old

3.6 火焰图

需要去github下载分析脚本

bash 复制代码
git clone https://github.com/brendangregg/FlameGraph.git

下载下来后,先使用perf record生成perf.data文件,例如输入如下指令

bash 复制代码
sudo perf record -e cpu-clock -g -p 10465

之后会根据perf.data进行解析,生成perf.unfold文件

bash 复制代码
perf script -i perf.data &> perf.unfold

perf.unfold中的符号进行折叠,生成perf.folded

bash 复制代码
FlameGraph/stackcollapse-perf.pl perf.unfold &> perf.folded

最后生成svg图

bash 复制代码
FlameGraph/flamegraph.pl perf.folded > perf.svg

也可以用参数-width-height来指定每一条的宽度和高度

bash 复制代码
FlameGraph/flamegraph.pl perf.folded > perf.svg -width 1000 -height 10

生成的火焰图,宽度越大表示CPU耗时越多

4. 实际操作

4.1 生成执行文件并放入设备运行

接下来进行一段代码实战,来感受一下实际过程中如何查看代码性能并定位。

首先在Ubuntu上写测试代码test.cpp,如下所示,即多加几个循环,foo()中调用bar()do_main()调用foo(),最后在while(1)中调用do_main()。由代码可以看出来,foo()循环时间要长一些,因此理应在该函数的时间最长,其次是bar()

cpp 复制代码
#include <iostream>
#include <vector>
#include <string>
#include <unistd.h>
using namespace std;

void bar(){
  for(int i=0;i< 4000;i++)
  {

  }
}

void foo(){
  for(int i=0;i< 5700;i++)
  {
      
  }      
  bar();
}

void do_main() {
  foo();
}

int main(int argc,char** argv){
    while(1)
    {
        do_main();
        // cout << "hello" << endl;
    }
}

然后使用编译链编译该cpp文件

bash 复制代码
g++ Desktop/test.cpp -o Desktop/testLinux

然后我们在后台运行起这个文件

bash 复制代码
.Desktop/testLinux &

4.2 生成perf.data文件

首先查看进程号

bash 复制代码
ps -xu | grep testLinux

可以使用perf top 实时查看一下

bash 复制代码
sudo perf top -e cpu-clock -p 4502

可以看到foo()函数占用的时间最多,其次是bar(),和我们最初的想法一致。按下ctrl+c即可退出返回至控制台

下面来生成perf.data文件

bash 复制代码
perf record -e cpu-clock -g -p 4502

使用report查看

bash 复制代码
perf report

4.3 生成火焰图

解析perf.data文件

bash 复制代码
perf script -i perf.data &> FlamGraph/result/perf.unfold

将unfold文件的字符进行压缩

bash 复制代码
FlameGraph/stackcollapse-perf.pl FlamGraph/result/perf.unfold &> FlamGraph/result/perf.folded

生成火焰图

bash 复制代码
FlameGraph/flamegraph.pl FlamGraph/result/perf.folded > FlamGraph/result/perf.svg

结果如下所示:

火焰图上面是bar(),然后其组成了foo()的全部,如果我们要优化代码的话可以从这两个函数下手。

5. 问题

  1. 问题 :输入perf record指令后且没有指定sleep时间,进程一直在阻塞状态
    解决:按下ctrl+c,即可生成perf.data文件

  2. 问题 :执行FlameGraph/flamegraph.plFlameGraph/stackcollapse-perf.pl的过程中出问题
    解决 :使用chmod 777 将这两个文件的权限改一下

  3. 问题 :最后生成的svg中,文字显示failed to open perf.data: Permission denied

    解决 :将perf.data文件用chmod 777 修改一下权限

参考资料

[1] perf性能分析工具使用分享

相关推荐
梁萌15 分钟前
Linux安装Docker
linux·运维·docker·helloworld·容器化部署
翱翔-蓝天18 分钟前
在 CentOS 系统上安装 ClickHouse
运维·docker·容器
AITIME论道22 分钟前
论文解读 | EMNLP2024 一种用于大语言模型版本更新的学习率路径切换训练范式
人工智能·深度学习·学习·机器学习·语言模型
彩虹糖_haha1 小时前
Linux高并发服务器开发 第五天(压缩解压缩/vim编辑器/查找替换/分屏操作/vim的配置)
linux·运维·服务器
旺仔学IT1 小时前
Centos7中使用yum命令时候报错 “Could not resolve host: mirrorlist.centos.org; 未知的错误“
linux·运维·centos
编程之路,妙趣横生1 小时前
list模拟实现
c++
qq_433618442 小时前
shell 编程(五)
linux·运维·服务器
VVVVWeiYee2 小时前
项目2路由交换
运维·服务器·网络·网络协议·信息与通信
Amd7943 小时前
PostgreSQL 初始化配置设置
postgresql·性能优化·用户管理·日志管理·安全性·数据库配置·初始设置
青春男大3 小时前
java栈--数据结构
java·开发语言·数据结构·学习·eclipse