perf初探

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


perf初探
https://cj0510.github.io/2026/08/30/Linux Kernel/perf/p0_perf/
作者
CJ1018
发布于
2026年8月30日
许可协议
CC BY-NC-SA 4.0