4. CPU调频与调度协同:cpufreq、schedutil governor、负载跟踪(PELT/WALT)

CPU调频(DVFS)与任务调度是Android性能优化的两个核心杠杆。传统方案中,调频与调度各自为政,导致响应延迟或功耗浪费。本章深入剖析Linux内核中cpufreq子系统、schedutil governor以及负载跟踪机制(PELT/WALT)如何协同工作,实现“调度感知调频”,这是现代Android内核性能优化的基石。

4.1 cpufreq子系统架构

cpufreq是Linux内核中管理CPU频率和电压的核心框架。它通过governor(调控器)决定何时调整频率,并通过driver(驱动)操作硬件寄存器。

核心概念: cpufreq将“策略”与“硬件操作”解耦。Governor计算目标频率,Driver执行频率切换。

4.1.1 关键组件与数据流

组件 角色 典型实现
cpufreq_policy 管理单个CPU簇(cluster)的频率范围、可用governor、关联CPU 每个性能域一个policy
cpufreq_governor 根据CPU负载或调度提示,计算目标频率 schedutil, interactive, ondemand
cpufreq_driver 封装硬件操作:设置频率、查询可用频率表 cpufreq-dt, acpi-cpufreq
cpufreq_stats 记录各频率驻留时间,用于功耗统计 /sys/devices/system/cpu/cpuX/cpufreq/stats
cpufreq 子系统架构与数据流 用户空间 (sysfs) cpufreq 核心层 Governor (schedutil) cpufreq_driver CPU 硬件 (P-State) 调度器 (负载跟踪) schedutil 直接读取 调度器的负载信号

4.2 schedutil governor:调度感知调频

schedutil 是 Linux 内核 4.7+ 引入的 governor,它直接与 CFS 调度器集成,利用调度器维护的负载跟踪数据(PELT/WALT)来驱动频率决策,消除了传统 governor 的采样延迟。

4.2.1 工作原理

  1. 负载更新事件驱动: 每次调度 tick 或任务唤醒/睡眠时,调度器更新 CPU 运行队列的负载。
  2. 频率计算: schedutil 在调度器更新负载后,立即根据当前负载和可用频率表计算目标频率。
  3. 快速切换: 通过 cpufreq_driver->fast_switch 接口,在原子上下文中直接切换频率,避免工作队列延迟。
性能优势: schedutil 的调频延迟通常在微秒级,而传统 ondemand governor 的采样周期为毫秒级。对于 Android 的触摸响应、应用启动等短时突发负载,schedutil 能更快地提升频率。

4.2.2 关键参数与调优

参数 路径 (sysfs) 说明 推荐值 (Android)
up_rate_limit_us .../schedutil/up_rate_limit_us 频率提升的最小间隔,防止频繁升频 500-1000
down_rate_limit_us .../schedutil/down_rate_limit_us 频率降低的最小间隔,防止频繁降频 2000-5000
iowait_boost_enable .../schedutil/iowait_boost_enable 是否在 IO 等待时临时提升频率 1 (开启)
schedutil 调频决策流程 调度事件 (tick/唤醒) 更新 PELT/WALT 负载 计算目标频率 fast_switch 切换频率 关键点: 无采样周期 事件驱动 原子上下文切换

4.3 负载跟踪:PELT vs WALT

负载跟踪是 schedutil 的“眼睛”。Android 内核主要使用两种负载跟踪算法:PELT(Per-Entity Load Tracking)和WALT(Window-Assisted Load Tracking)。Google 从 Android 10 开始逐步从 WALT 迁移回 PELT,但许多厂商仍在使用 WALT 或混合方案。

4.3.1 PELT(Per-Entity Load Tracking)

PELT 是主线 Linux 内核的标准负载跟踪算法。它通过半衰期衰减的数学公式,跟踪每个调度实体(task 或 runqueue)的负载贡献。

  • 核心公式: L = L * y + delta,其中 y = 0.5^(1/32),即每 32ms 负载衰减一半。
  • 优点: 数学优雅,时间连续,主线内核原生支持,维护成本低。
  • 缺点: 对短时突发负载(如触摸点击)反应不够灵敏,因为衰减曲线需要时间爬升。

4.3.2 WALT(Window-Assisted Load Tracking)

WALT 是 Qualcomm 为移动设备开发的负载跟踪算法,被 Google 用于 Android 内核(android-4.4 至 android-10)。

  • 核心思想: 将时间划分为固定窗口(通常 20ms),统计窗口内任务的执行时间。
  • 优点: 对短时突发负载响应极快,一个窗口内即可反映负载变化,非常适合交互式场景。
  • 缺点: 窗口边界效应导致负载抖动,多窗口累积逻辑复杂,与主线内核差异大,合并上游补丁困难。

4.3.3 对比总结

特性 PELT WALT
时间模型 指数衰减,连续时间 固定窗口,离散时间
突发响应 较慢(需多个 decay 周期) 快(一个窗口内即可反映)
负载稳定性 平滑,抖动小 窗口边界处可能抖动
主线兼容性 高(主线原生) 低(Android 特有)
功耗表现 频率变化较平缓,功耗更优 频率可能频繁跳变,功耗略高
Android 现状 Android 10+ 默认 Android 9 及之前默认,部分厂商继续使用
PELT vs WALT 负载跟踪对比 PELT (指数衰减) 负载 突发到达 负载缓慢爬升 衰减平滑 历史影响持久 WALT (固定窗口) 负载 突发到达 窗口内立即反映 窗口边界可能跳变 窗口结束负载归零 Android 内核选择演变 Android 4.4 - 9: WALT (Qualcomm 主导,交互优先) Android 10+: PELT (Google 推动,主线对齐) 厂商混合方案: 部分场景用 WALT,部分用 PELT 关键指标: 响应延迟 vs 功耗稳定性

4.4 调度与调频的协同机制

schedutil 与 CFS 调度器的深度协同,体现在以下几个关键路径:

4.4.1 任务唤醒路径

当一个任务被唤醒(如触摸中断处理完成),调度器会调用 enqueue_task_fair,更新运行队列负载。schedutil 的钩子函数 cpufreq_update_util 被触发,立即计算并设置更高的频率,为即将执行的任务做好准备。

4.4.2 IO 等待 boosting

当任务因 IO 阻塞而睡眠,唤醒后 schedutil 会临时提升频率(iowait boost),帮助任务快速处理 IO 完成后的数据。这对于 Android 的存储性能至关重要。

调优陷阱: iowait boost 如果设置过高或持续时间过长,会导致 CPU 频率长时间维持在高位,造成不必要的功耗。建议结合具体 IO 延迟数据(如 f2fs 的 iostat)进行微调。

4.4.3 迁移与簇间协同

在大小核架构(big.LITTLE)中,任务可能在不同簇之间迁移。schedutil 需要与 Energy-Aware Scheduling (EAS) 配合,确保任务迁移后,目标簇的调频策略能快速响应新任务的负载。

调度与调频协同:任务唤醒路径 硬件中断 (触摸) 唤醒任务 (wake_up) 更新 rq 负载 (PELT) schedutil 提升频率 协同效果: 任务被唤醒时 CPU 频率已提前提升 任务开始执行时 无需等待调频采样

4.5 实战:Android 平台调优建议

性能优化 checklist:
  • 确认当前使用的 governor:cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
  • 确认负载跟踪算法:检查内核配置 CONFIG_SCHED_WALT 是否开启
  • 调整 schedutil 的 rate_limit_us 参数,平衡响应速度与频率稳定性
  • 监控 /proc/sched_debug 和 /sys/kernel/debug/sched_features 中的负载信息

理解 cpufreq、schedutil 和负载跟踪的协同关系,是进行 Android 内核 CPU 调优的前提。下一层,我们将深入 IO 路径,探索存储性能的优化之道。