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、异步化
记住一个简单法则:user + system 占满,iowait 很低 → 计算密集;user 不高,但 iowait 或上下文切换高 → 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。

我的惯用流程是这样:

  1. 先跑 perf top 实时看热点
  2. 再 perf record 采集几秒
  3. 最后 perf report 分析调用链

比如这个例子,我想看 SDK 里最耗 CPU 的函数:

# 采样
perf record -g -F 99 ./sdk_worker --duration=10
# 生成报告
perf report -g graph

-F 99 表示每秒采样 99 次,-g 开启调用栈记录。输出结果会按 CPU 周期从高到低排列,点击每个函数还能展开父/子调用关系。

我最常用的 perf 命令速查:
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 record,因为大部分 SDK 在低负载下热点分布不明显。压力上去后,瓶颈才会暴露。

最后,记得配合 perf stat 看硬件事件。比如:

perf stat -e cycles,instructions,cache-misses ./sdk_worker

如果 cache-misses 高 + IPC(指令每周期)低,说明程序大概率在等内存——这又是另一种性能特征,跟计算密集/IO 密集不完全重合,但在实际项目中经常出现。

避坑指南:我曾经在云虚拟机里用 perf,发现 cycles 统计严重不准确。因为虚拟机下的硬件 PMU 通常被屏蔽或模拟,优先在物理机或裸金属容器里跑 perf。如果不得不用虚拟机,建议用 perf stat -d 并对比多次结果,看相对趋势而非值。
CPU 性能分析三步走 感知性能问题 定性:计算密集 vs IO 密集 系统级:/proc/stat 函数级:perf 采样 汇总热点 & 优化 先定性(计算 vs IO),再用 /proc/stat 看系统级趋势,最后用 perf 定位函数级热点 —— 这就是我每次做 CPU 性能分析的标准流水线