CPU性能瓶颈定位
做系统性能优化,CPU永远是第一个需要盯住的指标。我见过太多同学一上来就搞内存、搞IO,结果折腾半天发现是CPU调度出了问题——说白了,方向都没对。今天咱们就把CPU这层彻底讲透。
CPU使用率分析:别只看百分比
很多人一听到CPU瓶颈,第一反应就是看top。嗯,这没错,但光看%CPU是不够的。我记得有一次线上服务CPU打到80%,我上去一看user态占70%,sys态才不到10%,当时直觉告诉我不是内核问题。结果用perf top一看,是某个业务线程在死循环里做字符串拼接。你说这怪谁?
常用的几个命令,我列一下:
top/htop—— 全局视图vmstat 1—— 看上下文切换、中断、用户/系统时间/proc/stat—— 手动解析,适合脚本监控pidstat -u 1—— 按线程看利用率
这里有个坑:CPU使用率低不代表没瓶颈。比如大量线程在等锁,user态可能很低,但调度开销巨大。我曾经被一个"CPU才30%,但响应时间超时"的问题搞了三天……后来发现是futex竞争导致调度延迟爆炸。
我的习惯:先跑top看总的idle和sys/user比例,然后立刻切到pidstat -t看哪个线程在消耗CPU。如果sys过高(>30%),多半是系统调用或中断问题,往下看。
调度延迟:被忽视的隐形杀手
你做Android系统优化,调度延迟绕不过去。调度延迟说白了就是:线程已经准备好了,但CPU还在干别的事,没轮到它。这个时间一长,用户就感觉到“卡”。
怎么测?我用得最多的是两个方法:
- perf sched —— 记录调度事件,然后生成延迟分布图。
- Tracepoint sched_switch —— 配合atrace或者systrace,能看到每个线程被踢出去、被唤醒的时间点。
举个例子,你用perf sched record跑一段时间:
perf sched record -- sleep 5
perf sched latency
输出的表里会看到最大延迟、平均延迟、等待次数。我通常重点看“wait”列,如果某个线程平均等待超过10ms,那这个线程的响应性就已经有问题了。
我曾经踩过的坑:有一次我优化一个GPU渲染线程,发现它总是被一个后台GC线程抢占。看调度延迟表才发现,GC线程的优先级居然被设成了比渲染线程还高。你说这是不是低级错误?但工具不会骗你,一看数据就明白了。
irq/softirq:硬件和内核的“刺客”
irq(硬件中断)和softirq(软中断)是CPU被动的开销。你在top里看到si和hi两项,分别就是软中断和硬中断。
irq过多的典型场景:网卡中断、IO中断、定时器中断。我记得有一次在车载系统上,GPS模块每秒产生几千次中断,CPU被irq占掉了40%,导致UI线程调度延迟飙到几十毫秒。后来我通过/proc/interrupts找到罪魁祸首,然后用irqbalance把中断分散到不同核心,才压下去。
检查命令:
cat /proc/interrupts | head -20 # 看每个CPU上的中断次数
cat /proc/softirqs # 软中断分布
表格对比一下这两种中断:
| 类型 | 来源 | 典型占比 | 排查工具 |
|---|---|---|---|
| 硬中断(irq) | 硬件设备(网卡、磁盘、定时器) | 高负载下可达20-30% | /proc/interrupts |
| 软中断(softirq) | 内核下半部(网络收包、时钟、RCU) | 通常<5%,异常时飙升 | /proc/softirqs, /proc/stat |
如果发现某个CPU上irq特别多,可以用mpstat -I 1实时观察,或者用irqbalance重新分配。但在Android上,有些平台限制了irqbalance,那就得手动设置CPU affinity。
perf事件采样:终极定位武器
前面的方法都是敲门砖,真要到细节层面,还得靠perf的事件采样。perf的威力在于:它能告诉你CPU到底在"跑什么代码",而不是"跑了多少"。这个区别很关键。
我最常用的perf套路:
- 用
perf top实时看热点函数 - 用
perf record -g加调用栈采样 - 用
perf report分析,找出最热路径
perf record -F 99 -g -- sleep 10
perf report --stdio
这里-F 99代表每秒采样99次(避开100Hz的别名问题),-g记录调用栈。输出里你会看到每个函数占CPU的百分比,以及它被谁调用的。
常用的事件有:cycles、instructions、cache-misses、branch-misses。我曾在优化AOSP的SurfaceFlinger时,发现cache-misses异常高,一查是某个高频调用的函数里有个大结构体没有对齐——改成cacheline对齐后,性能直接提升了15%。
一点建议:perf在Android上需要内核开启CONFIG_PERF_EVENTS。如果目标设备是userdebug版,通常没问题。但生产环境可能没有,那时你可以用simpleperf代替——它是Google为Android定制的,用法很相似。
你看,整个流程走下来,思路就很清晰了:先从宏观的使用率入手,发现异常后再根据特征走调度延迟、中断、perf采样三条路。这三条路不是互斥的——我经常同时开着vmstat和perf record,一边看全局一边抓细节。
最后说一句,工具只是拐杖,关键是你对CPU工作原理的理解。为什么中断会打断线程?为什么调度延迟会抖动?搞懂这些,遇到问题自然能快速判断方向。嗯,这一章先到这,把这几招练熟,下一层就该看内存了。