4、CPU性能调优:CPU频率调节、调度器选择、大小核架构优化
CPU调优这块,说实话是Android性能优化里最硬核的部分之一。我早年做功耗优化时,经常遇到一种情况:手机跑分很高,但用户一用就发热降频,体验反而更差。说白了,CPU调优就是在性能和功耗之间走钢丝。
今天咱们就聊聊三个核心方向:频率怎么调、调度器怎么选、大小核怎么用。嗯,每个点我都会结合自己踩过的坑来讲。
4.1 CPU频率调节:不只是“拉满”那么简单
很多人一提到性能优化,第一反应就是“把CPU频率拉满”。我刚开始也这么干过,结果呢?手机烫得能煎鸡蛋,续航直接腰斩。后来才明白,频率调节的核心是按需分配。
4.1.1 调频策略(Governor)的选择
Linux内核里提供了好几种调频策略,Android里常用的有这几个:
| Governor | 特点 | 适用场景 |
|---|---|---|
| performance | 一直跑最高频 | 跑分、游戏加载 |
| powersave | 一直跑最低频 | 待机、后台任务 |
| ondemand | 负载高时快速升频 | 日常使用(较老设备) |
| interactive | 根据用户交互预测升频 | Android 4.x~8.x 主流 |
| schedutil | 基于调度器负载信息调频 | Android 9+ 主流 |
我个人习惯用 schedutil。为什么?因为它能跟调度器深度配合,调频更精准。我记得在某个项目里,把 interactive 换成 schedutil 后,滑动流畅度提升了约8%,功耗反而降了5%。
查看当前调频策略:
adb shell cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
临时切换策略(需要root):
adb shell "echo schedutil > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor"
4.1.2 调频参数微调
光选策略还不够,参数也得调。以 schedutil 为例,有几个关键参数:
- up_rate_limit_us:升频延迟,默认5000us。调小能让CPU更快响应突发负载,但太激进会频繁跳频。
- down_rate_limit_us:降频延迟,默认10000us。调大可以避免负载波动时频繁降频。
- hispeed_load:触发最高频的负载阈值,默认90%。调低到80%能让重负载任务更快跑满。
我的经验:在游戏场景下,我会把 up_rate_limit_us 降到2000us,hispeed_load 降到85%。这样进游戏加载时,CPU能瞬间冲到最高频,减少卡顿感。但日常使用建议保持默认,否则续航会崩。
4.2 调度器选择:谁来决定下一个跑谁?
调度器负责决定“下一个该哪个任务上CPU跑”。这个选择直接影响响应速度和公平性。Android里主流调度器有这几个:
| 调度器 | 核心思想 | 优缺点 |
|---|---|---|
| CFS(完全公平调度) | 按权重分配时间片 | 公平性好,但交互响应不够快 |
| HMP(异构多处理) | 大小核独立调度 | 简单粗暴,但负载均衡差 |
| EAS(能耗感知调度) | 结合能耗模型调度 | 省电且流畅,Android 11+ 标配 |
现在新设备基本都是EAS了。EAS会为每个任务估算“跑在大核上要多少电,跑在小核上要多少时间”,然后选最优解。说白了,就是用最少的电干最多的活。
注意:EAS依赖CPU的能耗模型(Energy Model),如果模型不准,调度反而会变差。我曾经遇到过某款芯片的能耗模型参数写反了,导致小核任务被误判为更耗电,结果全往大核上塞,功耗直接炸了。后来手动校准了模型参数才解决。
4.2.1 调度器参数调优
EAS有几个关键参数可以调:
- sched_energy_aware:是否启用EAS,默认1(开启)。
- sched_boost:调度器升压等级,0~100。调高会让调度器更倾向于用大核。
- sched_migration_cost_ns:任务迁移成本,默认500000ns。调小会让任务更频繁地迁移到更优的核上。
# 查看EAS是否启用
adb shell cat /proc/sys/kernel/sched_energy_aware
# 临时调整调度器升压(需要root)
adb shell "echo 50 > /proc/sys/kernel/sched_boost"
避坑指南:我曾经把 sched_migration_cost_ns 调得太小,结果任务在大小核之间来回跳,反而增加了开销。建议保持默认,除非你很清楚自己在做什么。
4.3 大小核架构优化:让对的核干对的活
现在手机基本都是大小核架构,比如1+3+4(1个超大核+3个大核+4个小核)或者4+4。怎么用好这些核,是性能优化的关键。
4.3.1 任务分类与绑核
我的做法是把任务分成三类:
- 前台交互任务(UI线程、输入事件):绑到大核或超大核,保证响应速度。
- 后台计算任务(网络请求、数据处理):绑到小核,省电。
- 实时任务(音频、视频解码):绑到特定核,避免被其他任务干扰。
绑核可以用 taskset 命令,或者在代码里用 sched_setaffinity:
// C代码示例:将当前线程绑定到CPU0和CPU1
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(0, &cpuset);
CPU_SET(1, &cpuset);
sched_setaffinity(0, sizeof(cpuset), &cpuset);
查看CPU拓扑:
adb shell cat /sys/devices/system/cpu/cpu*/topology/core_id
查看每个核的频率范围:
adb shell cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_available_frequencies
4.3.2 大小核负载均衡
EAS调度器会自动做负载均衡,但有时候不准。我习惯手动干预:
- 前台应用:强制使用大核+超大核,小核只跑后台服务。
- 游戏场景:把渲染线程绑到超大核,逻辑线程绑到大核,音频线程绑到小核。
- 待机场景:所有任务都往小核上赶,大核直接休眠。
注意:绑核不是万能的。我见过有人把所有线程都绑到大核上,结果大核过热降频,性能反而比不绑还差。记住,过犹不及。
4.3.3 实战案例:优化一个视频播放器
我记得有个项目,视频播放器在低端机上卡顿。分析后发现,解码线程和UI线程都在小核上跑,互相抢资源。我的优化方案:
- 把解码线程绑到两个大核上(CPU4-5)。
- 把UI线程绑到超大核上(CPU6)。
- 把网络请求线程绑到小核上(CPU0-3)。
- 调整
schedutil的up_rate_limit_us到3000us,让解码线程能快速升频。
结果呢?卡顿率从15%降到了3%,功耗只增加了2%。嗯,这就是大小核优化的魅力。
4.4 总结与工具推荐
CPU性能调优,说白了就是三件事:频率调对、调度选对、核用对。我常用的工具有:
- Perfetto:看调度轨迹、频率变化,必装。
- Simpleperf:分析CPU热点函数。
- /proc/stat 和 /sys/devices/system/cpu:手动查看CPU状态。
最后一句:别迷信参数。每个芯片、每个系统版本都不一样。我的习惯是:先跑基准测试,再调参数,再跑测试,反复迭代。调优没有银弹,只有耐心。
好了,CPU调优就聊到这儿。下一章咱们讲GPU和显示优化,那个更刺激——毕竟用户对卡顿的感知,80%来自显示。