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

每一列的含义:

列号名称说明
1user用户态时间(包括nice)
2nice低优先级用户态时间
3system内核态时间
4idle空闲时间
5iowait等待I/O完成的时间
6irq硬中断处理时间
7softirq软中断处理时间
8steal被虚拟机偷走的时间

计算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=不可中断睡眠)

htoptop的增强版,支持彩色显示、树形视图、鼠标操作。不过Android默认不带htop,需要自己编译或使用busybox版本。我个人更喜欢htop,因为它能直观显示每个CPU核心的使用情况。

实战技巧:我排查性能问题时,通常先开top看整体负载,然后按P键按CPU排序,找到最耗CPU的进程。接着用strace跟踪这个进程的系统调用,看它在干什么。三步走,基本能定位90%的问题。

综合实战:一次线上卡顿排查

最后分享一个真实案例。有一次用户反馈说手机在打开相机后特别卡。我按以下步骤排查:

  1. 看负载dumpsys cpuinfo显示Load 4.5,而设备只有4核,说明CPU过载
  2. 找元凶top发现mediaserver进程CPU占用35%,异常高
  3. 深挖原因strace -p [mediaserver的PID]发现它在反复读取某个大文件
  4. 定位问题:原来是相册应用在后台生成缩略图,导致mediaserver频繁I/O
  5. 解决方案:限制相册后台活动,优化缩略图生成策略

你看,整个排查过程就是围绕CPU频率、负载、进程这几个核心要素展开的。掌握了这些工具和方法,你也能快速定位CPU性能问题。

好了,这一章的内容就到这里。下一章我们会深入内存分析,讲讲怎么用dumpsys meminfo/proc/meminfo来排查内存泄漏。到时候见。