Skip to content

性能剖析实战

100 天认知提升计划 | Day 8


目录


第一部分:性能剖析基础

perf 工具原理

概念说明

perf 是 Linux 内核官方性能剖析器,位于内核源码的 tools/perf 目录。它是一个统一的前端接口,整合了内核中多种性能测量机制。

核心操作模式

模式命令开销说明
计数perf stat最低事件聚合统计,执行结束打印摘要
采样perf record中等按频率捕获事件详情和调用栈
追踪perf record -e <event>最高捕获每一次事件发生

事件来源

CPU 火焰图

发明者背景

火焰图由 Brendan Gregg 发明,他是系统性能优化领域的顶级专家,曾任职于 Sun Microsystems、Netflix、Intel,2026 年 2 月加入 OpenAI 专攻 ChatGPT 性能优化。

工作原理

火焰图阅读指南

维度含义分析技巧
横轴宽度采样次数占比(非时间轴)越宽 = CPU 占用越高,优先优化
纵轴深度调用栈深度层数多 = 嵌套调用复杂
颜色默认无特殊含义仅区分不同函数
顶部宽块函数自身耗时多密集计算、未优化算法
峡谷突变I/O 或锁等待write() 卡住导致堆积

生成火焰图完整流程

bash
# 1. 采样(99Hz,捕获调用栈)
sudo perf record -F 99 -ag -- sleep 30

# 2. 转换为折叠格式
sudo perf script | ./FlameGraph/stackcollapse-perf.pl > out.folded

# 3. 生成 SVG 火焰图
./FlameGraph/flamegraph.pl out.folded > flamegraph.svg

# 4. 差分火焰图(对比优化前后)
./FlameGraph/difffolded.pl old.folded new.folded | ./flamegraph.pl > diff.svg

第二部分:eBPF 与 bpftrace

eBPF 架构

核心概念

eBPF (Extended Berkeley Packet Filter) 是 Linux 内核中的一个虚拟机,允许用户在内核空间安全、高效地运行自定义程序。

执行全流程

执行阶段详解

阶段说明关键组件
编译C/脚本 → eBPF 字节码Clang/LLVM
加载提交字节码到内核libbpf, bpf() 系统调用
验证安全性检查BPF 验证器
JIT字节码 → 机器码JIT 编译器

bpftrace 探针机制

探针类型选择

探针类型描述稳定性适用场景
tracepoint内核静态跟踪点★★★★★通用系统事件,优先选择
fentry/fexit快速内核函数探针★★★★☆高频内核函数跟踪
kprobe/kretprobe内核动态探针★★☆☆☆任意内核函数,可能失效
uprobe/uretprobe用户态探针★★☆☆☆应用程序性能分析

bpftrace 程序结构

c
preamble     // 序言:类型、宏定义

probe[,probe]      // 探针:事件类型
/ predicate / {    // 谓词:过滤条件
  action          // 动作:数据处理
}

实战示例

bash
# 1. CPU 火焰图(99Hz 采样)
bpftrace -e 'profile:hz:99 /pid == 1234/ { @[ustack] = count(); }'

# 2. off-CPU 分析(阻塞原因)
bpftrace -e 'kprobe:schedule { @[kstack, ustack, comm] = count(); }'

# 3. 内存分配追踪
bpftrace -e 'u:libc:malloc { @[comm, ustack] = count(); }'

# 4. 系统调用统计
bpftrace -e 'tracepoint:syscalls:sys_enter* { @[probe] = count(); }'

Map 数据结构

Map 类型用途性能特点
Hash Map键值存储动态扩容,通用性强
Array固定大小索引访问最快,已知数量
Per-CPU Array无锁并发高性能计数器
Stack Trace调用栈存储函数调用链分析

第三部分:内存分析实战

用户态内存调试

Valgrind 工具集

工具功能使用场景
Memcheck内存错误检测泄漏、越界、未初始化
Callgrind函数调用分析CPU 时间、调用次数
Massif堆内存分析内存使用峰值
Helgrind线程竞争检测死锁、数据竞争
bash
# 1. 检测内存泄漏
valgrind --leak-check=full --show-leak-kinds=all ./program

# 2. 函数调用分析
valgrind --tool=callgrind ./program
kcachegrind callgrind.out.<pid>

# 3. 堆内存分析
valgrind --tool=massif ./program
ms_print massif.out.<pid>

内核态内存分析

问题定位流程

内核内存泄漏诊断

bash
# 1. 观察 Slab 变化
watch -n 5 'cat /proc/meminfo | grep -E "MemFree|Slab|SUnreclaim"'

# 2. slabtop 定位暴涨缓存
slabtop -o  # 按 Shift+P 排序

# 3. 检查具体 cache
grep dentry /proc/slabinfo

# 4. kmemleak 扫描
echo scan > /sys/kernel/debug/kmemleak
cat /sys/kernel/debug/kmemleak

# 5. perf 内存分配追踪
perf record -e kmem:kmalloc,kmem:kfree -a sleep 30
perf script | grep -E "kmalloc|kfree"

关键指标解读

指标正常状态异常信号
SUnreclaim/Slab< 50%> 90% → 大量对象无法回收
ACTIVE/OBJS稳定稳定增长 → 只分配不释放
PageTables平稳持续上涨 → 进程数暴增

实践与思考

实践记录

  • [ ] 任务 1:生成一个简单程序的 CPU 火焰图

    • 编写一个计算密集型程序(如递归斐波那契)
    • 使用 perf record 采样 30 秒
    • 生成火焰图并分析热点函数
  • [ ] 任务 2:使用 bpftrace 追踪系统调用

    • 编写 bpftrace 脚本统计 syscalls 频率
    • 添加过滤条件只追踪特定进程
    • 生成直方图查看调用分布
  • [ ] 任务 3:检测一个程序的内存泄漏

    • 编写一个故意泄漏内存的 C 程序
    • 使用 valgrind 定位泄漏点
    • 修复后验证结果
  • [ ] 任务 4:off-CPU 分析

    • 使用 bpftrace 追踪 schedule 函数
    • 分析进程阻塞的调用栈
    • 找出阻塞的根本原因
  • [ ] 任务 5:生成差分火焰图

    • 对优化前后代码分别采样
    • 生成差分火焰图对比
    • 量化性能改进效果

疑问与思考

已解答

  1. perf 和 valgrind 的区别?

    • perf:基于硬件计数器采样,关注 CPU 性能和热点,开销低
    • valgrind:用户态模拟执行,检测内存错误,开销高(10-30x)
  2. 为什么火焰图横轴不是时间轴?

    • 火焰图是对所有采样点的无序聚合
    • 横轴宽度代表采样次数占比
    • 同一行的函数按宽度排序,而非时间顺序
  3. bpftrace vs BCC 选择?

    • bpftrace:单行脚本,快速诊断,学习曲线低
    • BCC:Python 编写,复杂逻辑,生产环境工具开发

待探索

  1. ❓ eBPF 验证器的具体安全检查规则是什么?

    • 如何确保程序不会导致内核崩溃?
    • 哪些操作被验证器禁止?
  2. ❓ 在容器环境中使用 perf 有哪些限制?

    • 容器内能否获取完整的性能数据?
    • 需要哪些特殊权限?
  3. ❓ Brendan Gregg 加入 OpenAI 后的 AI 性能优化方向?

    • GPU 集群性能分析与传统 CPU 有何不同?
    • 神经网络的性能瓶颈在哪里?
  4. ❓ JIT 编译后的 eBPF 程序性能损失有多大?

    • 与原生内核代码相比的效率差距?

参考资料

官方文档

推荐书籍

在线资源


更新日期:2026-02-25

用 ❤️ 和 AI 辅助学习