目录

一、前言-perf 工具简介

二、使用场景——何时使用 perf

1、程序 CPU 占用高,不知道热点在哪

2、程序「感觉慢」,但 strace 看不出 syscall 瓶颈

3、优化前后效果验证

4、多线程 / 锁竞争 / 上下文切换问题

5、缓存未命中、分支预测失败(深度优化)

6、内核 + 用户态联合分析

7、C/C++ 程序无源码时的性能摸底

三、核心原理——工作原理深度解析

3.1 基本架构

3.2 两种主要工作模式

(1)计数模式(perf stat)

(2)采样模式(perf record / perf top)

3.3 事件来源

3.4 调用栈如何获得?(-g 的关键)

四、命令参数——常用选项详解

五、使用实战——实际案例分析

六、常见问题——疑难解答

七、总结——要点回顾


一、前言-perf 工具简介

    perf 是 Linux 内核自带的 性能分析工具,基于内核的 perf_events 子系统,可采集 CPU 周期、指令、缓存缺失、分支预测失败、系统调用、函数调用链等事件,用于 定位热点函数、分析 CPU 瓶颈、排查性能退化。

二、使用场景——何时使用 perf

1、程序 CPU 占用高,不知道热点在哪

    典型现象:top 显示进程 CPU 90%+,但不知道哪个函数耗 CPU;多线程程序某个线程跑满。

2、程序「感觉慢」,但 strace 看不出 syscall 瓶颈

    典型现象:strace -c 显示 syscall 总耗时不高,程序仍然慢。

3、优化前后效果验证

    典型现象:改了算法/数据结构,想量化提升;需要对比两个版本的 CPU 分布。

4、多线程 / 锁竞争 / 上下文切换问题

    典型现象:CPU 不高但整体吞吐低;怀疑 spinlock、futex、线程切换过多。

5、缓存未命中、分支预测失败(深度优化)

    典型现象:算法已优化,性能仍不理想;怀疑 cache miss、false sharing。

6、内核 + 用户态联合分析

    典型现象:驱动、网络、存储程序慢;不确定时间耗在用户态还是内核态。

7、C/C++ 程序无源码时的性能摸底

    典型现象:第三方 .so / 闭源二进制慢;只有符号表或部分符号。

三、核心原理——工作原理深度解析

    perf 的核心是 Linux 内核的 perf_events 子系统:在内核中注册性能监控事件,按计数或采样方式采集数据,写入 perf.data,再由用户态工具解析展示。

3.1 基本架构

3.2 两种主要工作模式

(1)计数模式(perf stat)
  • 对指定事件 全程计数
  • 输出汇总:cycles、instructions、cache-misses 等
  • 低开销,适合看整体指标
(2)采样模式(perf record / perf top)
  • 每隔 N 个事件(如每 4000 个 cycles) 采一次样
  • 记录:PC(程序计数器)、进程、线程、调用栈
  • 统计哪些函数出现频率高 → 热点

3.3 事件来源

类型 说明 示例

Hardware(PMU)

CPU 硬件性能计数器

cycles, instructions, cache-misses

Software

内核软件事件

context-switches, page-faults

Tracepoint

内核静态跟踪点

syscalls:sys_enter_open, sched:sched_switch

Probe

动态探针(kprobe/uprobe)

指定函数入口/返回

3.4 调用栈如何获得?(-g 的关键)

C/C++ 程序要看到 函数名 而非地址,需要:

  1. 编译时加 -g(保留 DWARF 调试信息)
  2. perf record 加 -g(采集 call graph)
  3. 系统允许读取符号(/proc/sys/kernel/perf_event_paranoid

四、命令参数——常用选项详解

    命令用法 usage: perf [--version] [--help] [OPTIONS] COMMAND [ARGS]

参数 说明
annotate 读取 perf.data(由 perf record 生成),并显示 带热点标注的源码/汇编
archive 根据 perf.data 中的 build-id 打包目标文件,便于 离线分析
bench 基准测试套件 的通用框架
buildid-cache 管理 build-id 缓存
buildid-list 列出 perf.data 文件中的 build-id
c2c 共享数据 C2C/HITM 分析器(分析多核缓存行竞争、伪共享等)
config 读取/设置 perf 配置文件 中的变量
data 与 perf.data 数据文件 相关的处理操作
diff 读取多个 perf.data,显示 差异性能报告(优化前后对比)
evlist 列出 perf.data 中记录的 事件名称
ftrace 内核 ftrace 功能的简易封装
inject 过滤并 向事件流注入额外信息
kallsyms 在 运行中的内核 里搜索符号
kmem 跟踪/测量 内核内存 相关特性
kvm 跟踪/测量 KVM 客户机(Guest OS)
list 列出所有可用的 符号化事件类型
lock 分析 锁相关事件(锁竞争等)
mem 分析 内存访问 行为
record 运行命令并将性能数据 记录到 perf.data
report 读取 perf.data 并 显示性能分析报告
sched 跟踪/测量 调度器 特性(延迟等)
script 读取 perf.data 并以 脚本/文本形式 输出跟踪内容
stat 运行命令并收集 性能计数器统计
test 运行 自检/健全性测试
timechart 将负载运行期间的 系统整体行为可视化(时间线图表)
top 系统性能实时分析 工具(类似 top,但看热点)
version 显示 perf 可执行文件版本
probe 定义新的 动态跟踪点(kprobe/uprobe 等)
trace 受 strace 启发 的工具(syscall 跟踪,开销通常低于 strace)

五、使用实战——实际案例分析

代码实例:perf_demo.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <math.h>

/* 热点1:CPU 密集 */
static volatile double sink;

static void cpu_hot(void)
{
    double s = 0;
    int i = 0;
    for (i = 0; i < 50000000; i++)
        s += sin(i * 0.000001);
    sink = s;
}

/* 热点2:内存拷贝 */
static void mem_hot(void)
{
    int i = 0;
    char *a = malloc(4 * 1024 * 1024);
    char *b = malloc(4 * 1024 * 1024);
    if (!a || !b) return;
    for (i = 0; i < 100; i++)
        memcpy(b, a, 4 * 1024 * 1024);
    free(a);
    free(b);
}

/* 非热点:sleep IO 等待 */
static void io_wait(void)
{
    sleep(1);
}

int main(void)
{
    printf("perf demo pid=%d\n", getpid());
    cpu_hot();
    mem_hot();
    io_wait();
    printf("done, sink=%f\n", (double)sink);
    return 0;
}

1、perf stat——整体性能画像

perf stat -d ./perf_demo

sleep(1) 期间 几乎不占 cycles(perf stat 主要统计运行态)

大量 cycles 在 cpu_hot + mem_hot

下一步 → perf record -g 看具体函

2、perf record + report——定位热点函数

perf record -g -F 997 ./perf_demo

perf report --stdio -n --sort dso,symbol | head -30

阶段 现象 对应源码

热点1

cpu_hot + sin 占 ~75%

cpu_hot() 循环

热点2

memcpy 占 ~18%

mem_hot() 百次 4MB 拷贝

非热点

sleep 几乎不出现

IO 等待不占 CPU

3、perf top——实时看谁在跑

./perf_demo &

perf top -p $! -g

运行初期 sincpu_hot 条目飙升

随后 memcpy 占比上升

sleep 阶段 perf top 样本几乎不

4、perf annotate——热点到源码行

perf record -g ./perf_demo

perf annotate -s cpu_hot

精确到 哪一行汇编/源码 最耗 CPU,指导优化(如减少 sin 调用、查表法)。

六、常见问题——疑难解答

1、perf report 只显示地址,没有函数名

原因:编译未加 -g;二进制被 strip;权限/paranoid 限制

2、提示 Permission denied / Access denied

原因: kernel.perf_event_paranoid 过严,或需 root 采系统级事件。

3、调用栈不完整或显示 [unknown]

原因:栈回溯方式不合适(frame pointer 被优化掉);缺少 -g 或 -fno-omit-frame-pointer

4、perf 结果「看起来」和 top 不一致

原因:perf 默认采 cycles,top 看 %CPU;多线程、采样频率、off-CPU 时间影响

七、总结——要点回顾

  1. perf 跟踪的是 CPU 性能事件和热点函数,回答「时间花在哪些函数/指令上」,而不是「发起了哪些 syscall」。

  2. 原理是 perf_events 采样/计数,开销通常低于 ptrace 型 strace,适合 短时性能分析,但仍不建议 7×24 全开。

  3. C/C++ 分析三板斧:

    perf stat ./app # 整体指标

    perf record -g ./app # 录热点

    perf report # 看报告

  4. 务必 -g 编译 + perf record -g,必要时 -fno-omit-frame-pointer,否则调用栈残缺。

  5. 与 strace 配合最高效: strace 排除 syscall/IO 问题 → perf 定位 CPU 热点 → gdb 深入具体函数。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐