4. EAS调度器原理:能量感知调度模型、CPU容量与任务利用率、能量计算模型

好,咱们今天聊点硬核的——EAS调度器。

说实话,我在做手机性能调优的头两年,对调度器的理解还停留在“哪个核空着就往哪塞”的阶段。直到有一次,客户反馈某款旗舰机刷抖音半小时就烫手,我抓了trace一看,小核负载明明很低,系统却把UI线程往大核上扔……嗯,从那以后,我才真正开始啃EAS的源码。

EAS,全称Energy-Aware Scheduling,能量感知调度。它不是凭空冒出来的,而是Linux内核在CFS基础上做的一次“绿色升级”。说白了,就是让调度器不光看“哪个CPU跑得快”,还要看“哪个CPU跑得省电”。

4.1 能量感知调度模型:从“性能优先”到“能效优先”

传统的CFS调度器,核心逻辑是负载均衡。它会把任务尽量均匀地分配到所有CPU上,保证每个核的利用率差不多。这有什么问题?

我举个例子。你有一个轻量级任务,比如处理一个触摸事件,只需要10%的CPU算力。CFS可能会把它放到一个空闲的大核上。大核跑10%的负载,功耗可能是小核跑10%负载的3倍。你想想看,这电是不是白费了?

EAS的思路完全不同。它维护了一个“能效模型”,知道每个CPU在不同频率下的功耗是多少。调度时,它会算一笔账:

  • 这个任务需要多少算力?(任务利用率)
  • 哪个CPU能提供这个算力,而且最省电?(能量计算)

所以,EAS本质上是一个“成本-收益”分析器。它把每个CPU都标了价签,调度时选最便宜的。

核心思想:EAS不是追求“最快完成”,而是追求“在满足性能要求的前提下,能耗最低”。

4.2 CPU容量与任务利用率:两个核心度量

要理解EAS怎么算账,必须先搞懂两个概念:CPU容量和任务利用率。

4.2.1 CPU容量(CPU Capacity)

CPU容量,你可以理解为这个CPU的“最大力气”。它不是频率,也不是主频,而是一个归一化的值。

在ARM大小核架构(比如big.LITTLE)中,通常把小核的容量设为256,大核的容量可能是1024。为什么是这些数字?因为内核里用arch_scale_cpu_capacity()这个函数来算,它考虑了微架构、最高频率、以及一些硬件特性。

我习惯把CPU容量看作“天花板”。一个容量为1024的大核,理论上能提供的最大算力是小核的4倍。但注意,这只是理论值,实际还要受频率、热限制等因素影响。

CPU类型 典型容量值 说明
小核(Cortex-A55) 256 能效高,算力低
中核(Cortex-A78) 512 平衡点
大核(Cortex-X1/X2) 1024 性能强,功耗高

避坑指南:我曾经在调试一款联发科平台时发现,CPU容量值被厂商改成了非标准值。大核容量设成了800,小核设成了200。这导致EAS误判,以为大核没那么强,结果任务都往中核上堆,中核直接过热。所以,拿到一个新平台,第一件事就是检查/sys/devices/system/cpu/cpu*/cpu_capacity

4.2.2 任务利用率(Task Utilization)

任务利用率,就是“这个任务吃了多少CPU”。它由PELT(Per-Entity Load Tracking)算法计算得出,是一个0到1024之间的值。

举个例子。一个任务在100ms的窗口内,实际运行了50ms,那么它的利用率就是512(50%)。如果它一直跑,利用率就是1024(100%)。

EAS用这个值来估算:如果把这个任务放到某个CPU上,它会占用该CPU多少比例的容量。

公式很简单:

任务所需容量 = 任务利用率 × CPU最大容量 / 1024

比如,一个利用率为512的任务,放到容量为1024的大核上,它需要512的容量。放到容量为256的小核上,它需要512的容量——但小核只有256,所以放不下!

注意:任务利用率是历史统计值,不是瞬时值。它有一个衰减机制,旧的影响会逐渐变小。所以,一个突然爆发的任务,利用率不会立刻冲到1024,而是慢慢爬升。这会导致EAS在任务刚启动时做出“过于乐观”的决策。

4.3 能量计算模型:EAS怎么算电费?

好,现在我们有CPU容量和任务利用率了。EAS怎么决定把任务放哪?它要算“能量消耗”。

能量计算模型的核心是:能量 = 功率 × 时间

但EAS不直接算功率,它用了一个更巧妙的办法——查表。

4.3.1 能量模型表(Energy Model Table)

内核里维护了一张表,记录了每个CPU在不同频率下的功率(单位通常是毫瓦)。这张表由芯片厂商提供,放在设备树或ACPI中。

我见过最典型的表长这样:

CPU 频率 (MHz) 功率 (mW)
小核0 500 50
小核0 1000 120
大核4 1000 300
大核4 2000 800

EAS在做调度决策时,会做以下几步:

  1. 预估频率:根据任务利用率,算出如果放到这个CPU上,需要把频率调到多高才能满足需求。
  2. 查功率:从能量模型表中,查出该频率对应的功率。
  3. 算能量:能量 = 功率 × 预估运行时间。但EAS简化了,它直接用“功率 × 利用率”来估算。

说白了,EAS会遍历所有可能的CPU,对每个CPU算出一个“能量代价”,然后选代价最小的那个。

4.3.2 一个简单的计算示例

假设我们有一个任务,利用率为256(25%负载)。有两个候选CPU:

  • 小核:容量256,跑25%负载时频率500MHz,功率50mW。
  • 大核:容量1024,跑25%负载时频率500MHz,功率200mW(因为大核即使低频,静态功耗也高)。

EAS会算:

  • 放小核:能量 ≈ 50mW × 0.25 = 12.5
  • 放大核:能量 ≈ 200mW × 0.25 = 50

显然,放小核更省电。EAS就会把这个任务放到小核上。

关键点:EAS的能量计算不是精确的物理计算,而是一个“相对比较”。它不关心绝对功耗值,只关心哪个方案更省电。所以,即使厂商提供的功率数据有偏差,只要相对关系正确,EAS就能正常工作。

4.4 EAS的决策流程:一张图看懂

说了这么多,我画了一张流程图,帮你把整个逻辑串起来。

EAS调度决策流程图 新任务/任务唤醒 步骤1:获取任务利用率(PELT) 步骤2:遍历所有CPU,计算所需容量 步骤3:查能量模型表,预估频率和功率 步骤4:计算每个CPU的能量代价 选择能量最小的CPU

4.5 实际项目中的坑与经验

EAS理论听起来很完美,但在实际手机项目中,我踩过不少坑。分享几个给你:

  • 能量模型表不准:有些厂商为了省事,直接拿公版数据。但不同芯片的漏电流差异很大,导致EAS算出来的“最优解”其实是错的。我建议拿到新平台后,用功耗仪实测几个典型场景,反向校准能量模型表。
  • 任务利用率抖动:PELT的衰减周期是32ms。如果一个任务每30ms爆发一次,利用率会忽高忽低,导致EAS频繁迁移任务。迁移本身也有功耗开销(cache miss等)。我曾经遇到一个游戏场景,EAS每秒迁移任务上百次,功耗反而比不开EAS还高。解决办法是给EAS加一个“迁移代价”的惩罚项。
  • 大小核容量差距过大:如果大核容量是小核的8倍以上(比如X4超大核配A55小核),EAS会倾向于把所有任务都往小核上塞,因为小核太省电了。结果小核满载,大核空转,性能反而下降。这时候需要手动调整容量值,或者用uclamp(利用率钳制)来限制任务能使用的CPU范围。

我的习惯:在调试EAS时,我通常会先关掉所有调频和热控,只让EAS做调度决策。然后跑一个固定的负载,看任务是不是被分配到了预期的CPU上。如果不对,先查能量模型表,再查CPU容量值。90%的问题都出在这两个地方。

好了,EAS的原理就讲到这里。它不是一个万能药,但确实是目前手机SoC调度领域最先进的方案之一。理解它的核心逻辑——能量感知、容量匹配、代价计算——你就能在调优时做到心中有数。


专注资料整理