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

前言

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

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

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可以列出所有的采样事件。

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常用选项如下所示:

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

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

perf record -e event -g -p grep your_program

   perf record常用选项如下所示:

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

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

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选项,可以在终端上执行命令时收集性能统计信息

   该指令的选项如下所示:

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

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

ps aux

在这里插入图片描述

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

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

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使用

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

perf top

在这里插入图片描述

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

第一列显示给定函数正使用的CPU百分比
第二列显示使用函数的程序或库的名称
第三列显示函数名称或符号,内核空间中执行的功能由[k]标识,用户空间中执行的功能则用[.]标识

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

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

3.5 perf diff使用

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

sudo perf diff perf.data perf.data.old

3.6 火焰图

   需要去github下载分析脚本

git clone https://github.com/brendangregg/FlameGraph.git

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

sudo perf record -e cpu-clock -g -p 10465

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

perf script -i perf.data &> perf.unfold

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

FlameGraph/stackcollapse-perf.pl perf.unfold &> perf.folded

   最后生成svg图

FlameGraph/flamegraph.pl perf.folded > perf.svg

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

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()

#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文件

g++ Desktop/test.cpp -o Desktop/testLinux

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

.Desktop/testLinux &

4.2 生成perf.data文件

   首先查看进程号

ps -xu | grep testLinux

在这里插入图片描述

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

sudo perf top -e cpu-clock -p 4502

在这里插入图片描述

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

   下面来生成perf.data文件

perf record -e cpu-clock -g -p 4502

   使用report查看

perf report

在这里插入图片描述

4.3 生成火焰图

   解析perf.data文件

perf script -i perf.data &> FlamGraph/result/perf.unfold

   将unfold文件的字符进行压缩

FlameGraph/stackcollapse-perf.pl FlamGraph/result/perf.unfold &> FlamGraph/result/perf.folded

   生成火焰图

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性能分析工具使用分享

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值