perf基础
Perf 是 Linux 内核提供的一套性能分析工具,它通过 Linux perf_event 子系统获取 CPU、内核和程序运行时的性能数据。
其原理大致如下

PMU全称Performance Monitoring Unit,性能监控单元,位于CPU内部,可以提供硬件性能事件统计能力。
perf的功能很强大,其基本功能大致如下

Perf是用户态工具,它自己不会凭空产生性能数据。
数据主要来自CPU硬件和Linux内核,再由perf_event子系统统一提供给Perf。

用户态程序通过系统调用perf_event_open向内核申请一个性能事件
1 2 3 4 5
| int perf_event_open(struct perf_event_attr *attr, pid_t pid, int cpu, int group_fd, unsigned long flags);
|
上面函数会返回一个文件描述符fd
这里只简单描述一下,有个概念,之后深入原理在讲解。
Perf主要有两种基础观察模式分别是Counting和Sampling,
前者观察一共发生了多少次,
后者观察这些事件主要发生在哪里。
其整体工作流程如下:

perf的一般命令
| 子命令 |
主要回答的问题 |
perf list |
Perf事件 |
perf stat |
整体性能事件 |
perf top |
现在谁最耗CPU |
perf record |
把采样数据记录下来 |
perf report |
哪些函数和调用路径是热点? |
perf script |
把采样记录展开成文本 |
perf sched |
调度过程和延迟 |
perf trace |
系统调用或事件 |
perf stat
用于量化程序整体性能,我们看一下描述
1
| stat Run a command and gather performance counter statistics
|
就是统计这个程序运行期间发生了多少性能事件。
举个例子,一个test.c的程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #include <stdint.h> #include <stdio.h>
int main(void) { volatile uint64_t sum = 0; uint64_t i;
for (i = 0; i < 10000000; ++i) sum += i;
printf("perf test OK, sum=%llu\n", (unsigned long long)sum); return 0; }
|
在泰山派上运行后
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| root@taishanpi:/data# perf stat ./test perf test OK, sum=49999995000000
Performance counter stats for './test':
41.79 msec task-clock # 0.945 CPUs utilized 在CPU上的运行时间 8 context-switches # 191.419 /sec 上下文切换次数 0 cpu-migrations # 0.000 /sec CPU迁移次数 46 page-faults # 1.101 K/sec 缺页异常次数 52936193 cycles # 1.267 GHz CPU时钟周期 60626751 instructions # 1.15 insn per cycle 指令个数IPC 10069652 branches # 240.940 M/sec 分支指令个数 11852 branch-misses # 0.12% of all branches
0.044236500 seconds time elapsed # 程序开始到结束总时间
0.043817000 seconds user # 用户态代码运行时间 0.000000000 seconds sys # 内核态运行时间
|
可以看到其核心功能就是计数Counting,其关注的是总共发生了多少次。
用过-e指定事件
1 2 3 4 5 6 7 8 9 10 11
| root@taishanpi:/data# perf stat -e instructions ./test perf test OK, sum=49999995000000
Performance counter stats for './test':
60589231 instructions
0.039916625 seconds time elapsed
0.040039000 seconds user 0.000000000 seconds sys
|
我们看其中的一个重要指标IPC,其表示CPU平均每个时钟周期完成了多少条机器指令。
IPC的计算公式如下

perf top
用于显示CPU当前主要在执行哪些函数,实时采样CPU正在执行的位置,然后按函数统计并显示热点。
其使用的方式是Sampling。其默认采样事件为cycles。他采集是指令地址IP
看个例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| #include <stdio.h>
static void busy_loop(unsigned long long loops) { volatile unsigned long long sum = 0;
for (unsigned long long i = 0; i < loops; i++) { sum += i; } }
static void foo(void) { busy_loop(700000000ULL); }
static void bar(void) { busy_loop(200000000ULL); }
static void baz(void) { busy_loop(100000000ULL); }
int main(void) { while (1) { foo(); bar(); baz(); }
return 0; }
|
从调用者的角度看,大致比例是:foo 70%;bar 20%;baz 10%。
上机测试

[.]:用户态符号
[k]:内核态符号
perf record/report
对目标程序或系统进行性能事件采样,并把采集到的样本保存到文件。
举例
1
| ./perf record -e cycles -F 99 -g -o record_test.data -- ./record_test 1
|
record:采集性能采样数据
-e cycles:根据 CPU 周期采样
-F 99:每秒约采样 99 次
-g:记录调用栈
-o record_test.data:指定输出文件
--:后面是被测试程序及其参数
./record_test 10:运行约 10 秒
查看
1 2 3 4
| # 交互式查看 ./perf report -i record_test.data # 非交互式输出: ./perf report --stdio -i record_test.data
|
生成火焰图
1 2 3
| perf script \ -i record_test.data \ > record_test.perf
|
下载 FlameGraph
1
| git clone https://github.com/brendangregg/FlameGraph.git
|
生成
1 2 3 4 5 6 7 8
| perl FlameGraph/stackcollapse-perf.pl \ record_test.perf \ > record_test.folded
perl FlameGraph/flamegraph.pl \ --title "record_test CPU Flame Graph" \ record_test.folded \ > record_test.svg
|
