本文将介绍如何使用Perf工具对Linux应用程序进行分析,并以Jetson NX为例演示如何通过源码在无二进制支持的平台上编译Perf程序,并结合Clion进行简单的可视化性能分析。

性能分析的意义

在一些高性能要求的C程序中,我们总是要评估程序的运行时间。最简单的办法就是在程序中打印执行时间。但这种方法非常繁琐,统计粒度很大,程序中也会多出很多无业务意义的统计代码,对开发和编译带来负担。而性能分析工具(profile tool)能够在不影响源代码的情况下,在运行时对进程进行采样分析,并且能够覆盖绝大部分的程序堆栈(只要能跟踪到)。

在实际的使用中,profile tool能够反映出程序执行热点,能够客观地指导我们对程序的优化工作。例如,我们之前认为在图像处理程序中比较占用时间的是降采样的过程,但实际上程序热点是在一些矩阵的数学计算上。这就为我们之后有针对性地优先优化矩阵运算提供了指导方向。

Linux上的性能分析

众所周知,被誉为“宇宙第一IDE”的Visual Studio中具有非常强大的性能分析工具。那么在Linux中有没有呢?当然是有的,目前主流的一些工具是PerfgprofValgrind。在本文中,我们主要介绍Perf

Perf是基于Linux内核的性能分析工具,其原理是抽样统计。Perf每隔一段时间就会在CPU的每个核上产生一个中断,并在中断上查看此时对应的是哪个PID的哪个函数(还有线程)。基于这种采样的模式,我们能够预期,运行时间越多的函数,被时钟中断击中的机会越大,从而推测,哪个函数(或者PID等)的CPU占用率就越高。

但也要注意的是,如果某个函数的运气特别好,每次都躲过了中断,那么在结果中对于该函数的统计就是有问题的。所以在用Perf测试的时候,要让采样数尽可能多(在系统资源允许的时候),并且最好多测几次。

Perf的安装

Perf的源代码就在Linux的源码中,但是Linux源码在编译时是不会编译Perf的,一般需要自行到子目录下进行编译。此外,由于perf的源代码使用了一些Linux内核的头文件;又由于Perf作为Linux主推的性能分析工具,更新非常迅速;因此perf的编译版本是和内核版本强绑定的。

软件包安装

对于绝大部分通用设备,都是能够直接使用软件仓库进行安装的。只需使用uname -r找出当前内核版本,然后安装对应的linux-tools包。例如:

$ uname -r
4.15.0-36-generic
$ sudo apt-get install linux-tools-4.15.0-36-generic

源码方式安装

对于嵌入式设备,内核很可能是定制的。Jetson处理单元就是这样,使用的是L4T,tegra版本的内核。对于这种设备,通用的软件仓库中是没法找到对应的Perf版本的。对于这种情况,我们需要下载对应平台的内核源码进行编译(直接使用/usr/src/linux-headers-*内的源码是不行的,对于NX来说)。

使用以下命令查询当前内核的PERF支持特性

$ gunzip -c /proc/config.gz | grep PERF
CONFIG_CGROUP_PERF=y
CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=y
CONFIG_HAVE_PERF_EVENTS=y
CONFIG_PERF_EVENTS=y
# CONFIG_DEBUG_PERF_USE_VMALLOC is not set
CONFIG_HAVE_PERF_REGS=y
CONFIG_HAVE_PERF_USER_STACK_DUMP=y
# CONFIG_PCIEASPM_PERFORMANCE is not set
CONFIG_HW_PERF_EVENTS=y
CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
# CONFIG_CLS_U32_PERF is not set
# CONFIG_BCMDHD_CUSTOM_NET_PERF_TEGRA is not set
CONFIG_DEVFREQ_GOV_PERFORMANCE=y
# CONFIG_RCU_PERF_TEST is not set
CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y

下载内核源码

对于Jetson设备,我们首先需要使用命令查询L4T版本

$ head -n 1 /etc/nv_tegra_release
# R32 (release), REVISION: 5.1, GCID: 26202423, BOARD: t186ref, EABI: aarch64, DATE: Fri Feb 19 16:50:29 UTC 2021

我们就可以知道,我们当前的L4T版本为32.5.1,那么我们就可以在L4T Archive | NVIDIA Developer中找到对应的版本链接打开,并下载 Xavier NX列下的L4T Driver Package (BSP) Sources

编译Perf

我们可以参考以下命令进行编译,注意,该源码包对应的是R32.5.1的L4T源码。

$ sudo apt install -y libperl-dev
$ mkdir l4t && cd l4t
$ wget https://developer.nvidia.com/embedded/l4t/r32_release_v5.2/sources/t186/public_sources.tbz2
$ tar -xjf public_sources.tbz2
$ tar -xjf Linux_for_Tegra/source/public/kernel_src.tbz2
$ cd kernel/kernel-4.9/tools/perf
$ make
$ ./perf --version

注意,在编译时,make脚本会检查相关依赖项。如果确实一些依赖项,会导致一些特性无法使用。make脚本在这之后会提示需要安装的依赖库,使用apt等工具安装就可以了。

Perf编译时的依赖检查

如果检查的版本类似于下面这样,没有显示其它东西,则说明该构建能够运行(但特性不一定正常)

$ ./perf --version
perf version 4.9.201

内核选项调整

我们需要执行以下指令以让非ROOT用户能够访问性能事件数据:

$ sudo sh -c 'echo 1 >/proc/sys/kernel/perf_event_paranoid'
$ sudo sh -c 'echo 0 >/proc/sys/kernel/kptr_restrict'

其中:

  • perf_event_paranoid - 控制非 root 用户对性能事件数据的使用。
  • kptr_restrict - 设置对暴露内核地址的限制。

如果需要在重启后继续保持该设置,请执行以下命令。但不建议持久化该配置,因为可能会降低系统安全性。

sudo sh -c 'echo kernel.perf_event_paranoid=1 >> /etc/sysctl.d/99-perf.conf'
sudo sh -c 'echo kernel.kptr_restrict=0 >> /etc/sysctl.d/99-perf.conf'
sudo sh -c 'sysctl --system'

此外,Clion在启动分析器时也会自动检查这些配置,并会建议进行更改。

在Clion上进行性能分析

具体的项目配置内容和使用方法可参见Profiler | Clion,具体操作方法就不赘述了。提几个点:

  • 测试代码最好是放在Debug下执行,这样才能跟踪到尽可能完整的堆栈。
  • 当然Release的测试也要做,但应该不是日常开发模式。
  • 同理,GCC优化别开太猛。
  • 有时候统计不一定准,最好多测几次,有些情况可能会失效(参见下面一些文档)。
  • 采样率按照Clion文档,不要设成整数,尽量避免与其它中断冲突。
  • 日志如果不保存的话就放/tmp目录,勾选退出时删除。
  • perf程序路径记得别弄错喽。
  • Clion在2021.2版本中已经支持了远程开发中的性能分析。

参考文档

  1. Profiler | Clion
  2. Linux性能分析工具Perf简介(原文:Tutorial - Perf Wiki
  3. 在Linux下做性能分析3:perf
  4. 在 Jetson TX2 上安装 Linux 性能监控工具 - Nvidia开发者论坛
  5. Linux性能分析工具与图形化方法
Last modification:August 31st, 2021 at 01:53 pm