CPU性能分析:频率与调度、负载计算、实战工具
各位同学,今天我们来聊聊CPU性能分析。说实话,CPU这块是Android性能优化的硬骨头。我刚开始做系统优化时,也踩过不少坑。比如有一次线上反馈说手机发烫,我查了半天,最后发现是某个后台进程把CPU频率锁死在了最高档——嗯,这种问题光靠看代码是看不出来的。
CPU性能分析,说白了就是搞清楚三件事:CPU跑多快、谁在用、用多少。下面我们一个一个来看。
CPU频率与调度:系统的心脏跳动
CPU频率,就是CPU的工作时钟速度。现代Android设备都支持动态调频,也就是根据负载自动调整频率。我见过不少开发者以为频率越高越好,其实不是。频率高了,功耗就上去了,手机发热、掉电快,用户会骂娘的。
调频策略通常由内核的cpufreq框架管理。常见的governor有:
- performance:一直跑最高频,性能好但费电
- powersave:一直跑最低频,省电但卡顿
- interactive:根据负载动态调整,Android常用
- schedutil:基于调度器负载的调频策略,新内核标配
核心观点:频率不是越高越好,而是越匹配负载越好。调频策略的选择直接影响用户体验。
调度器方面,Android从Linux继承了CFS(完全公平调度器)。每个进程都有一个nice值,影响它的时间片分配。我习惯用renice命令来调整前台应用的优先级,比如:
# 将PID为1234的进程优先级调高(nice值越小优先级越高)
renice -n -10 -p 1234
但要注意,不要随意调整系统进程的nice值。我曾经手贱调了system_server的优先级,结果系统直接卡死重启——嗯,教训深刻。
负载计算:别被数字骗了
CPU负载,很多人以为就是CPU使用率。其实不是。负载是正在运行+等待运行的进程数。比如负载是2.5,意味着平均有2.5个进程在抢CPU。
Linux的负载计算方式是这样的:
- 每个CPU核心维护一个运行队列
- 每5秒采样一次队列长度
- 计算1分钟、5分钟、15分钟的加权平均值
你想想看,如果单核CPU的负载是1.0,说明CPU刚好跑满。超过1.0就说明有进程在排队了。我见过一些手机,负载动不动就飙到8.0以上,那用户体验可想而知——卡得跟幻灯片似的。
避坑指南:我曾经用top看CPU使用率只有30%,但手机就是卡。后来查了负载才发现是8.5。为什么?因为很多进程在等待I/O,CPU使用率不高,但负载很高。所以负载比使用率更能反映系统压力。
dumpsys cpuinfo:快速定位CPU消耗大户
dumpsys cpuinfo是Android自带的工具,可以查看各进程的CPU使用情况。用法很简单:
adb shell dumpsys cpuinfo
输出示例:
Load: 2.5 / 2.3 / 2.1
CPU usage from 0ms to 3000ms:
com.example.app: 12% = 8% user + 4% kernel / faults: 123 minor
system_server: 8% = 5% user + 3% kernel / faults: 45 minor
surfaceflinger: 5% = 3% user + 2% kernel / faults: 12 minor
...
这里要注意几个关键字段:
- Load:1分钟、5分钟、15分钟的平均负载
- user + kernel:用户态和内核态的CPU时间占比
- faults:缺页中断次数,数值高说明内存紧张
我个人习惯先看Load,如果超过核心数,说明CPU是瓶颈。然后看哪个进程的CPU占比异常高。有一次我发现某个第三方应用CPU占用持续30%以上,一查发现它在后台做视频编解码——直接卸载,问题解决。
/proc/stat解析:最底层的CPU数据
如果你想看更细粒度的CPU数据,/proc/stat是绕不开的。这个文件记录了系统启动以来的CPU时间统计。我写性能监控工具时,经常直接解析这个文件。
典型内容:
cpu 123456 7890 123456 7890123 4567 8901 2345 0 0 0
cpu0 61234 3890 61234 3945061 2234 4450 1172 0 0 0
cpu1 62222 4000 62222 3945062 2333 4451 1173 0 0 0
每一列的含义:
| 列号 | 名称 | 说明 |
|---|---|---|
| 1 | user | 用户态时间(包括nice) |
| 2 | nice | 低优先级用户态时间 |
| 3 | system | 内核态时间 |
| 4 | idle | 空闲时间 |
| 5 | iowait | 等待I/O完成的时间 |
| 6 | irq | 硬中断处理时间 |
| 7 | softirq | 软中断处理时间 |
| 8 | steal | 被虚拟机偷走的时间 |
计算CPU使用率的公式很简单:
CPU使用率 = (总时间 - 空闲时间) / 总时间 × 100%
其中总时间 = user + nice + system + idle + iowait + irq + softirq + steal。
注意:/proc/stat是累计值,要计算使用率必须采样两次,取差值。我见过有人直接拿一次的数据算,结果永远是100%——嗯,这错误我也犯过。
top与htop实战:实时监控利器
top是Linux最经典的进程监控工具。在Android上用法类似:
adb shell top -d 1 -n 10
参数说明:
-d 1:刷新间隔1秒-n 10:刷新10次后退出
输出示例:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1234 u0_a1 20 0 1.2G 120M 80M S 15.2 3.1 0:12.34 com.example.app
5678 system 18 -2 1.5G 200M 100M S 8.5 5.2 2:34.56 system_server
关键字段:
- %CPU:CPU使用率(多核下可能超过100%)
- %MEM:内存使用率
- S:进程状态(S=睡眠,R=运行,D=不可中断睡眠)
htop是top的增强版,支持彩色显示、树形视图、鼠标操作。不过Android默认不带htop,需要自己编译或使用busybox版本。我个人更喜欢htop,因为它能直观显示每个CPU核心的使用情况。
实战技巧:我排查性能问题时,通常先开top看整体负载,然后按P键按CPU排序,找到最耗CPU的进程。接着用strace跟踪这个进程的系统调用,看它在干什么。三步走,基本能定位90%的问题。
综合实战:一次线上卡顿排查
最后分享一个真实案例。有一次用户反馈说手机在打开相机后特别卡。我按以下步骤排查:
- 看负载:
dumpsys cpuinfo显示Load 4.5,而设备只有4核,说明CPU过载 - 找元凶:
top发现mediaserver进程CPU占用35%,异常高 - 深挖原因:
strace -p [mediaserver的PID]发现它在反复读取某个大文件 - 定位问题:原来是相册应用在后台生成缩略图,导致mediaserver频繁I/O
- 解决方案:限制相册后台活动,优化缩略图生成策略
你看,整个排查过程就是围绕CPU频率、负载、进程这几个核心要素展开的。掌握了这些工具和方法,你也能快速定位CPU性能问题。
好了,这一章的内容就到这里。下一章我们会深入内存分析,讲讲怎么用dumpsys meminfo和/proc/meminfo来排查内存泄漏。到时候见。