CPU性能分析:计算密集 vs IO密集,/proc/stat解读,使用perf采样热点函数
做 SDK 性能优化,CPU 是最先盯住的资源。但很多人一上来就盲目追指标,反而抓不住重点。我见过不少团队,明明瓶颈在 IO,却拼命优化计算逻辑,结果白费功夫。
这一章,我带你走一遍 CPU 分析的标准路径。先分清业务是计算密集还是 IO 密集,再借助 /proc/stat 和 perf 两个利器,把热点函数揪出来。
计算密集 vs IO 密集——先定性,再定量
你想想看,一个程序占满了 CPU,原因可能是它在疯狂计算,也可能是在等数据、等锁、等磁盘。这两种场景下的优化手段完全不同。
我自己的经验是:先通过几项基础指标快速定性。下面这张表,是我在项目中总结的判断清单:
| 维度 | 计算密集 | IO 密集 |
|---|---|---|
| CPU 使用率 | 持续 90%+,尤其 user 高 | 通常低于 60%,偶有尖刺 |
| 上下文切换 | 低(< 5000/s) | 高(> 15000/s,甚至更高) |
| iowait | 接近 0 | 明显 > 5% |
| 典型场景 | 音视频编解码、加密、矩阵运算 | 日志写入、网络收发、数据库查询 |
| 优化方向 | 算法、指令集、多核并行 | 减少传输、合并 IO、异步化 |
/proc/stat 解读——系统级 CPU 温度计
好,定性之后,我们需要更精细的数据。/proc/stat 是 Linux 内核暴露的 CPU 时间分布快照。每次文件打开,内核都会返回从启动到当前的累计 ticks。
cat /proc/stat | grep "^cpu "
# 输出示例:
cpu 1512345 12345 789012 99887766 56789 0 1234 0 0 0
这些数字分别对应(按顺序):
- user:用户态耗时
- nice:低优先级用户态耗时
- system:内核态耗时
- idle:空闲时间
- iowait:等待 IO 完成的时间
- irq:硬中断
- softirq:软中断
- steal:被虚拟机偷走的时间
- guest / guest_nice:虚拟 CPU 开销(通常为 0)
计算 CPU 使用率的公式很简单:
total = user + nice + system + idle + iowait + irq + softirq + steal
usage = (user + nice + system) / total * 100
/proc/stat 是 累计值,必须间隔 N 秒采样两次,计算差值然后用 delta 算。单次读数的值没有意义,我曾经见过新手直接拿第一眼的值推断负载,闹了笑话。
如果你觉得手动算麻烦,可以用 mpstat -P ALL 1,它本质上就是在读取 /proc/stat 并帮你做差分计算。
perf 采样热点函数——精确到代码行
/proc/stat 虽然是系统级法宝,但只能告诉你 CPU 花在哪个 大类 上。真要定位到函数级别,得用 perf。
我的惯用流程是这样:
- 先跑
perf top实时看热点 - 再
perf record采集几秒 - 最后
perf report分析调用链
比如这个例子,我想看 SDK 里最耗 CPU 的函数:
# 采样
perf record -g -F 99 ./sdk_worker --duration=10
# 生成报告
perf report -g graph
-F 99 表示每秒采样 99 次,-g 开启调用栈记录。输出结果会按 CPU 周期从高到低排列,点击每个函数还能展开父/子调用关系。
perf top # 实时热点(类似 top 但针对函数)
perf stat ./app # 统计 CPU周期、指令数、缓存未命中
perf record -g ./app # 采样并保存到 perf.data
perf report # 分析 perf.data
perf annotate <symbol> # 查看指定函数的汇编/源码热区
有一次,我发现 SDK 在接收网络包时 CPU 使用率异常高。用 perf top 看到一个函数占 30% 的 CPU,展开调用链发现它在 skb_copy_bits 里反复拷贝数据。后来改成零拷贝接收,CPU 直接降了 40%。这就是 perf 的威力——让你精准找到“真凶”。
最后,记得配合 perf stat 看硬件事件。比如:
perf stat -e cycles,instructions,cache-misses ./sdk_worker
如果 cache-misses 高 + IPC(指令每周期)低,说明程序大概率在等内存——这又是另一种性能特征,跟计算密集/IO 密集不完全重合,但在实际项目中经常出现。
cycles 统计严重不准确。因为虚拟机下的硬件 PMU 通常被屏蔽或模拟,优先在物理机或裸金属容器里跑 perf。如果不得不用虚拟机,建议用 perf stat -d 并对比多次结果,看相对趋势而非值。