perf 是内置于 Linux 内核源码树中的性能剖析(profiling)工具。
它基于事件采样原理,以性能事件为基础,支持针对处理器相关性能指标与操作系统相关性能指标的性能剖析。常用于性能瓶颈的查找与热点代码的定位。
1. 安装 perf
在大多数 Linux 发行版中,perf 工具包含在 linux-tools
包中。可以通过包管理器安装:
- Debian/Ubuntu:
bash
sudo apt-get install linux-tools-common linux-tools-generic
- Red Hat/CentOS:
bash
sudo yum install perf
2. 基本使用
列出可用事件
列出所有可用的性能事件(包括硬件和软件事件):
bash
perf list
记录性能数据
记录目标程序的性能数据。例如,以99Hz频率对所有CPU采样并记录调用栈信息6秒:
bash
perf record -g -a -F 99 sleep 6
可以指定特定事件类型来分析,如CPU时钟周期、缓存命中等:
bash
perf record -e cpu-clock -a sleep 6
跟踪点用于跟踪系统调用等预定义事件:
bash
perf record -e 'syscalls:sys_enter_*' -a sleep 6
perf record
的可选参数
参数 | 描述 | 示例 | 备注 |
---|---|---|---|
-e |
选择性能事件。可以是 CPU 周期、缓存未命中等。使用 perf list 命令来查看所有可用的事件。 |
perf record -e cpu-clock ./your_program |
适用于指定要监控的性能事件类型 |
-a |
记录整个系统的性能数据,而不仅仅是一个进程。这对于分析系统级的性能问题非常有用。 | perf record -a sleep 6 |
允许对整个系统的性能进行剖析 |
-p <pid> |
记录指定进程的性能数据。只关注某一个特定进程。 | perf record -p 12345 sleep 6 |
<pid> 是目标进程的进程ID |
-t <tid> |
记录指定线程的性能数据。用于分析多线程程序中某个特定线程的性能。 | perf record -t 12345 sleep 6 |
<tid> 是目标线程的线程ID |
-o <file> |
指定输出文件名。默认情况下,perf 会将数据保存在当前目录下的 perf.data 文件中。 |
perf record -o my_output.data ./your_program |
自定义性能数据的存储位置 |
-g |
记录函数调用关系(调用栈)。这对于分析程序中函数调用和热点非常有帮助。 | perf record -g ./your_program |
提供了函数调用链的信息,有助于理解代码执行路径 |
-c <count> |
设置事件的采样周期。例如,每发生1000次事件采样一次。 | perf record -c 1000 ./your_program |
控制基于计数器的采样频率 |
-F <frequency> |
设置事件的采样频率。例如,每秒采样1000次。 | perf record -F 1000 ./your_program |
控制基于时间的采样频率 |
使用建议
- 系统级分析 :如果你希望对整个系统进行性能分析,可以使用
-a
参数。 - 特定进程/线程分析 :对于专注于分析特定的进程或线程的情况,分别使用
-p
或-t
参数。 - 函数调用关系 :当需要理解程序的函数调用关系,尤其是在定位性能热点时,
-g
参数非常重要。 - 简单开始 :在实际使用中,一开始可以只用
perf record ./your_program
来进行简单的性能记录,然后根据具体需求添加不同的参数。
分析性能数据
使用 perf report
分析记录的数据,默认读取当前目录下的 perf.data
文件。可使用 -i
指定其他文件。
3. 使用示例
一个简单的 C++ 程序比较 push_back
和 emplace_back
的性能差异。编译和运行后,使用 perf 工具进行性能分析。
由于 emplace_back
避免了对象的复制,所以在这种情况下,它会显示出更好的性能。
-
push_back
的性能热点: -
ComplexObject::ComplexObject(int)
被频繁调用,且每次调用都伴随着内存分配和初始化。 -
构造函数和析构函数的调用占用了大量 CPU 时间(例如,
ComplexObject::ComplexObject(int)
占比 4.12%,ComplexObject::~ComplexObject()
占比 2.94%)。 -
std::vector::push_back
涉及到更多的复制或移动操作,这可能导致更高的开销。
ComplexObject::ComplexObject(int)
占比 4.71%,表示构造函数被频繁调用。std::vector<ComplexObject, std::allocator<ComplexObject> >::~vector()
占比 3.53%,表示析构函数也被频繁调用。void std::vector<ComplexObject, std::allocator<ComplexObject>>::push_back(ComplexObject const&)
占比 2.94%,这可能涉及到对象的复制或移动操作。
emplace_back
的性能优势:
emplace_back
直接在向量中构造对象,减少了临时对象的创建和销毁,从而降低了构造函数和析构函数的调用次数。- 这种方式避免了不必要的复制或移动操作,因此通常会更快。
4. 其他功能
- 实时性能监控 : 使用
perf top
查看实时性能热点。 - 注解(Annotate): 对特定函数或代码行进行源代码级别的性能分析。
- 事件计数 : 使用
perf stat
统计特定事件的发生次数。
5. 高级用法
- 调试符号: 确保程序带有调试符号以获得更详细的信息。
- CPU Cache 分析: 分析缓存使用情况。
- 硬件计数器: 分析底层事件如分支预测错误。
- 系统调用跟踪 : 使用
perf trace
跟踪系统调用。 - 脚本接口: 生成自定义报告。
6. 注意事项
调整 /proc/sys/kernel/perf_event_paranoid
和 /proc/sys/kernel/kptr_restrict
设置,允许非特权用户使用性能分析功能。
7. 可能遇到的问题
问题1: perf_event_paranoid 设置限制
根据错误信息,可能需要降低 perf_event_paranoid
设置值,以允许当前用户使用 perf 工具。可以临时或永久调整此设置。
问题2: kptr_restrict 设置导致内核样本无法解析
由于 kptr_restrict
设置,内核符号被限制访问。可以临时或永久调整此设置,或者确保有匹配的 vmlinux
文件用于解析内核样本。
注意:调整这些设置可能会带来安全风险,应谨慎操作,并考虑生产环境中的安全性需求。