调度器23—EAS-2-文档翻译—energy-model.rst&sched-energy.rst

简介:
msm-5.4/Documentation/power/energy-model.rst
msm-5.4/Documentation/scheduler/sched-energy.rst


一、energy-model.rst

====================
CPU 能耗模型
=====================

1. 概述

-----------

能耗模型 (EM) 框架作为驱动程序和内核子系统之间的接口。驱动程序了解 CPU 在不同性能级别下的功耗,而内核子系统则希望利用这些信息做出节能决策。

不同平台上 CPU 功耗信息的来源可能差异很大。在某些情况下,可以使用设备树数据估算这些功耗。在其他情况下,固件会更了解情况。或者,用户空间可能更合适。等等。为了避免每个客户端子系统都自行重新实现对每种可能信息来源的支持,EM 框架作为抽象层介入,它标准化了内核中功耗表的格式,从而避免了重复工作。

下图展示了一个驱动程序示例(此处以 Arm 为例,但该方法适用于任何架构),该驱动程序向 EM 框架提供功耗信息,而感兴趣的客户端则从中读取数据::

       +---------------+  +-----------------+  +---------------+
       | Thermal (IPA) |  | Scheduler (EAS) |  |     Other     |
       +---------------+  +-----------------+  +---------------+
               |                   | em_pd_energy()    |
               |                   | em_cpu_get()      |
               +---------+         |         +---------+
                         |         |         |
                         v         v         v
                        +---------------------+
                        |    Energy Model     |
                        |     Framework       |
                        +---------------------+
                           ^       ^       ^
                           |       |       | em_register_perf_domain()
                +----------+       |       +---------+
                |                  |                 |
        +---------------+  +---------------+  +--------------+
        |  cpufreq-dt   |  |   arm_scmi    |  |    Other     |
        +---------------+  +---------------+  +--------------+
                ^                  ^                 ^
                |                  |                 |
        +--------------+   +---------------+  +--------------+
        | Device Tree  |   |   Firmware    |  |      ?       |
        +--------------+   +---------------+  +--------------+

EM框架管理系统中每个“性能域”的功耗成本表(power cost tables)。性能域是指一组性能相同的CPU。性能域通常与CPU频率策略一一对应。同一性能域内的所有CPU必须具有相同的微架构。不同性能域中的CPU可以具有不同的微架构。


2. 核心 API

------------

2.1 配置选项

要使用 EM 框架,必须启用 CONFIG_ENERGY_MODEL


2.2 性能域注册

驱动程序需要通过调用以下 API 将性能域注册到 EM 框架中:

int em_register_perf_domain(cpumask_t *span, unsigned int nr_states, struct em_data_callback *cb);

驱动程序必须使用 cpumask 参数指定性能域的 CPUs,并提供一个回调函数,该函数返回每个容量状态(capacity state)的 <frequency, power> 对。驱动程序提供的回调函数可以自由地从任何相关位置(DT、固件等)获取数据,并可采用任何必要的方式。有关驱动程序实现此回调的示例,请参阅第 3 节;有关此 API 的更多文档,请参阅 kernel/power/energy_model.c。


2.3 访问性能域

对 CPU 能耗模型感兴趣的子系统可以使用 em_cpu_get() API 获取该模型。能耗模型表在创建性能域时分配一次,并永久保存在内存中。

可以使用 em_pd_energy() API 估算性能域的能耗。估算时假设使用了 schedutil CPUfreq 调速器。

有关上述 API 的更多详细信息,请参阅 include/linux/energy_model.h。


3. 驱动程序示例

-----------------

本节提供了一个简单的 CPUFreq 驱动程序示例,该驱动程序使用(伪)'foo' 协议在能源模型框架中注册性能域。该驱动程序实现了一个 est_power() 函数,该函数将提供给EM框架。

  -> drivers/cpufreq/foo_cpufreq.c

  01    static int est_power(unsigned long *mW, unsigned long *KHz, int cpu)
  02    {
  03        long freq, power;
  04
  05        /* Use the 'foo' protocol to ceil the frequency */
  06        freq = foo_get_freq_ceil(cpu, *KHz);
  07        if (freq < 0);
  08            return freq;
  09
  10        /* Estimate the power cost for the CPU at the relevant freq. */
  11        power = foo_estimate_power(cpu, freq);
  12        if (power < 0);
  13            return power;
  14
  15        /* Return the values to the EM framework */
  16        *mW = power;
  17        *KHz = freq;
  18
  19        return 0;
  20    }
  21
  22    static int foo_cpufreq_init(struct cpufreq_policy *policy)
  23    {
  24        struct em_data_callback em_cb = EM_DATA_CB(est_power);
  25        int nr_opp, ret;
  26
  27        /* Do the actual CPUFreq init work ... */
  28        ret = do_foo_cpufreq_init(policy);
  29        if (ret)
  30            return ret;
  31
  32        /* Find the number of OPPs for this policy */
  33        nr_opp = foo_get_nr_opp(policy);
  34
  35        /* And register the new performance domain */
  36        em_register_perf_domain(policy->cpus, nr_opp, &em_cb);
  37
  38            return 0;
  39    }


二、sched-energy.rst

=======================
节能调度
========================

1. 引言

---------------

节能调度(Energy Aware Scheduling,简称 EAS)使调度器能够预测其决策对 CPU 能耗的影响。EAS 依赖于 CPU 的能耗模型 (EM) 为每个任务选择节能型 CPU,同时最大限度地减少对吞吐量的影响。本文档旨在介绍 EAS 的工作原理、其背后的主要设计决策,以及运行 EAS 所需的配置。

在继续阅读之前,请注意,截至撰写本文时:

/!\ EAS 不支持具有对称 CPU 拓扑结构的平台 /!\

EAS 仅在异构 CPU 拓扑结构(例如 Arm big.LITTLE)上运行,因为在这种架构下,通过调度实现节能的潜力最大。

EAS 实际使用的能源模型并非由调度器维护,而是由一个专用框架维护。有关此框架及其功能的详细信息,请参阅其文档(参见 Documentation/power/energy-model.rst)。


2. 背景和术语

-----------------------------

首先明确以下概念:

- energy = [焦耳](例如,电池等供电设备中的资源)

- power = 能量/时间 = [焦耳/秒] = [瓦特]

EAS 的目标是在完成任务的前提下,最大限度地减少能量消耗。也就是说,我们希望最大化:

    performance [inst/s]
    --------------------
        power [W]

这等价于最小化::

    energy [J]
    -----------
    instruction

在保证良好性能的前提下,它本质上是调度器当前仅关注性能目标之外的另一种优化目标。这种替代方案同时考虑了能效和性能两个目标。

引入能量模型 (EM) 的目的是让调度器能够评估其决策的影响,而不是盲目地应用可能仅在某些平台上产生积极效果的节能技术。同时,能量模型必须尽可能简单,以最大限度地减少对调度器延迟的影响。

简而言之,能量效率优化 (EAS) 改变了分配 CFS 任务给 CPU 的方式。当调度器需要决定任务在哪个CPU上运行时(在唤醒期间),能量模型用于打破几个性能优异的 CPU 候选方案之间的僵局,并选择预计在不损害系统吞吐量的前提下实现最佳能耗的 CPU。能量效率优化 (EAS) 的预测依赖于有关平台拓扑结构的特定信息,包括 CPU 的“capacity”及其各自的能耗成本。


3. 拓扑信息

-----------------------

EAS(以及调度器的其他部分)使用“容量”的概念来区分具有不同计算吞吐量的 CPU。CPU 的“容量”表示它在最高频率下运行时能够处理的工作量,并与系统中性能最强的 CPU 进行比较。容量值被归一化到 1024 的范围内,并且与基于实体负载跟踪 (PELT) 机制计算的任务和 CPU 利用率信号具有可比性。借助容量和利用率值,EAS 能够估计任务/CPU 的大小/繁忙程度,并在评估性能与能耗之间的权衡时考虑这些因素。

CPU 的容量通过架构特定的代码,经由 `arch_scale_cpu_capacity()` 回调函数提供。

EAS 使用的其余平台知识直接读取自能量模型 (EM) 框架。平台的 EM 由系统中每个“性能域”的功耗成本表组成(有关性能域的更多详细信息,请参阅 Documentation/power/energy-model.rst)。

调度器在构建或重建调度域时,会在拓扑代码中管理对 EM 对象的引用。对于每个根域 (rd),调度器维护一个单链表,其中包含所有与当前 rd->span 相交的性能域。链表中的每个节点都包含一个指向 EM 框架提供的 struct em_perf_domain 的指针。

这些链表附加到根域是为了处理互斥 CPU 集配置。由于互斥 CPU 集的边界不一定与性能域的边界匹配,因此不同根域的链表可能包含重复元素。


示例 1.

假设我们有一个拥有 12 个 CPU 的平台,分为 3 个性能域(pd0、pd4 和 pd8),组织结构如下:

              CPUs:   0 1 2 3 4 5 6 7 8 9 10 11
              PDs:   |--pd0--|--pd4--|---pd8---|
              RDs:   |----rd1----|-----rd2-----|

现在,假设用户空间决定将系统拆分为两个互斥的 CPU 集,从而创建两个独立的根域,每个根域包含 6 个 CPU。这两个根域在上图中分别表示为 rd1 和 rd2。由于 pd4 与 rd1 和 rd2 都有交集,因此它将附加到每个根域的链表“->pd”中:

       * rd1->pd: pd0 -> pd4
       * rd2->pd: pd4 -> pd8

请注意,调度器会为 pd4 创建两个重复的列表节点(每个列表一个)。但是,这两个节点都只持有指向 EM 框架中同一共享数据结构的指针。


由于对这些列表的访问可能与热插拔和其他操作同时发生,因此它们像调度器操作的其他拓扑结构一样,需要受到 RCU 的保护

EAS 还维护一个静态键 (sched_energy_present),当至少一个根域满足 EAS 启动的所有条件时,该键将被启用。这些条件在第 6 节中进行了概述。


4. 节能任务放置

------------------------------

EAS 会覆盖 CFS 任务唤醒均衡代码。它利用平台的 EM 和 PELT 信号在唤醒均衡期间选择节能目标 CPU。启用 EAS 后,`select_task_rq_fair()` 会调用 `find_energy_efficient_cpu()` 来进行放置决策。该函数会在每个性能域中寻找剩余容量(CPU 容量 - CPU 利用率)最高的 CPU,因为只有这样的 CPU 才能将频率保持在最低水平。然后,该函数会检查将任务放置到该 CPU 上是否能够比将其留在 `prev_cpu`(即任务上次激活时运行的 CPU)上更节能。

`find_energy_efficient_cpu()` 函数使用 `compute_energy()` 函数来估算如果唤醒任务迁移(到我这个pd)了,系统将消耗多少能量。`compute_energy()` 函数会查看 CPU 的当前利用率情况,并进行调整以“模拟”任务迁移。EM 框架提供了 `em_pd_energy()` API,用于计算给定利用率情况下每个性能域的预期能耗。

下面详细介绍一个节能任务放置决策的示例。

示例 2:

假设我们有一个(虚拟的)平台,它有两个独立的性能域,每个性能域由两个 CPU 组成。CPU0 和 CPU1 是小型 CPU;CPU2 和 CPU3 是大型 CPU。

调度器必须决定将任务 P 放置在何处,该任务的 util_avg = 200 且 prev_cpu = 0。

下图展示了当前 CPU 的利用率情况。CPU 0-3 的 util_avg 分别为 400、100、600 和 500。每个性能域都有三个运行性能点 (OPP)。每个 OPP 对应的 CPU 容量和功耗列于“能量模型”表中。任务 P 的 util_avg 在下图中以“PP”表示。

     CPU util.
      1024                 - - - - - - -              Energy Model
                                               +-----------+-------------+
                                               |  Little   |     Big     |
       768                 =============       +-----+-----+------+------+
                                               | Cap | Pwr | Cap  | Pwr  |
                                               +-----+-----+------+------+
       512  ===========    - ##- - - - -       | 170 | 50  | 512  | 400  |
                             ##     ##         | 341 | 150 | 768  | 800  |
       341  -PP - - - -      ##     ##         | 512 | 300 | 1024 | 1700 |
             PP              ##     ##         +-----+-----+------+------+
       170  -## - - - -      ##     ##
             ##     ##       ##     ##
           ------------    -------------
            CPU0   CPU1     CPU2   CPU3

      Current OPP: =====       Other OPP: - - -     util_avg (100 each): ##

`find_energy_efficient_cpu()` 函数首先会在两个性能域中寻找剩余容量最大的 CPU。在本例中,这两个 CPU 分别是 CPU1 和 CPU3。然后,它会估算如果将 P 放置在其中任何一个 CPU 上,系统的能耗,并检查与将 P 放置在 CPU0 上相比,这样做是否能节省一些能耗。EAS 假设 OPP 遵循利用率(这与 schedutil CPUFreq 调速器的行为一致,更多详情请参见第 6 节)。

**情况 1:P 迁移到 CPU1**

      1024                 - - - - - - -

                                            Energy calculation:
       768                 =============     * CPU0: 200 / 341 * 150 = 88
                                             * CPU1: 300 / 341 * 150 = 131
                                             * CPU2: 600 / 768 * 800 = 625
       512  - - - - - -    - ##- - - - -     * CPU3: 500 / 768 * 800 = 520
                             ##     ##          => total_energy = 1364
       341  ===========      ##     ##
                    PP       ##     ##
       170  -## - - PP-      ##     ##
             ##     ##       ##     ##
           ------------    -------------
            CPU0   CPU1     CPU2   CPU3

**情况 2. P 迁移到 CPU3**::

      1024                 - - - - - - -

                                            Energy calculation:
       768                 =============     * CPU0: 200 / 341 * 150 = 88
                                             * CPU1: 100 / 341 * 150 = 43
                                    PP       * CPU2: 600 / 768 * 800 = 625
       512  - - - - - -    - ##- - -PP -     * CPU3: 700 / 768 * 800 = 729
                             ##     ##          => total_energy = 1485
       341  ===========      ##     ##
                             ##     ##
       170  -## - - - -      ##     ##
             ##     ##       ##     ##
           ------------    -------------
            CPU0   CPU1     CPU2   CPU3

**情况 3. P 停留在 prev_cpu / CPU 0**::

      1024                 - - - - - - -

                                            Energy calculation:
       768                 =============     * CPU0: 400 / 512 * 300 = 234
                                             * CPU1: 100 / 512 * 300 = 58
                                             * CPU2: 600 / 768 * 800 = 625
       512  ===========    - ##- - - - -     * CPU3: 500 / 768 * 800 = 520
                             ##     ##          => total_energy = 1437
       341  -PP - - - -      ##     ##
             PP              ##     ##
       170  -## - - - -      ##     ##
             ##     ##       ##     ##
           ------------    -------------
            CPU0   CPU1     CPU2   CPU3

根据这些计算结果,方案1的总能耗最低。因此,从能效角度来看,CPU 1是最佳选择。


大型 CPU 通常比小型 CPU 更耗电,因此主要用于小型 CPU 无法处理的任务。然而,小型 CPU 的能效并非总是优于大型 CPU。例如,在某些系统中,小型 CPU 的高频点反而可能比大型 CPU 的低频点更耗电。因此,如果小型 CPU 在特定时间点的利用率很高,已经运行在较高频点上,那么此时被唤醒的小型任务即使适合在小型 CPU 上运行,为了节省能源,也可能更适合在大型 CPU 上执行。

即使所有大CPU的运算功率(OPP)都低于小CPU,在特定条件下,使用大CPU执行小任务仍然可能节省能源。事实上,将任务放在小CPU上会提高整个性能域的运算功率,从而增加已在小CPU上运行的任务的成本。如果将唤醒任务放在大CPU上,其自身的执行成本可能高于在小CPU上运行,但不会影响小CPU上的其他任务,这些任务仍将以较低的运算功率运行。因此,考虑到CPU的总能耗,在大核心上运行该任务的额外成本可能小于提高小CPU上所有其他任务的运算功率所造成的成本。

如果不了解系统所有 CPU 在不同 OPP 下运行的成本,上述示例几乎不可能以通用方式在所有平台上正确实现。得益于其基于 EM 的设计,EAS 应该能够轻松应对这些问题。然而,为了确保在高利用率场景下对吞吐量的影响最小,EAS 还实现了另一种称为 “over-utilization 的机制


5. Over-utilization

-------------------

从总体上看,EAS 最能发挥作用的用例是 CPU 利用率较低或中等的场景。当运行长时间运行的 CPU 密集型任务时,它们会占用所有可用的 CPU 资源,而调度器在不严重影响吞吐量的情况下很难节省能源。为了避免 EAS 影响性能,一旦 CPU 的利用率超过其算力的 80%,就会被标记为“过载”。只要根域中没有 CPU 过载,负载均衡就会被禁用,EAS 会覆盖唤醒均衡代码。如果能在不影响吞吐量的情况下,EAS 可能会优先使用系统中能效最高的 CPU。因此,负载均衡器会被禁用,以防止它破坏 EAS 找到的节能任务放置方案。当系统没有过载时,这样做是安全的,因为低于 80% 的临界点意味着:

a. 所有 CPU 都存在一定的空闲时间,因此 EAS 使用的利用率信号很可能准确地反映了系统中各个任务的“规模”;

b. 所有任务都应该已经分配了足够的 CPU 资源,无论它们的 nice 值如何;

c. 由于存在剩余资源,所有任务都必须定期处于阻塞/睡眠状态,因此唤醒时的资源均衡就足够了。

一旦某个 CPU 的使用率超过 80% 的临界点,上述三个假设中至少有一个将不再成立。在这种情况下,整个根域的“overutilized”标志将被设置,EAS 将被禁用,负载均衡器将被重新启用。这样一来,在 CPU 密集型情况下,调度器将回退到基于负载的算法来进行唤醒和负载均衡。这能更好地保证任务的 nice 值得到满足

由于过载的概念很大程度上依赖于检测系统中是否存在空闲时间,因此必须考虑被更高层级(高于 CFS)调度类(以及 IRQ)“占用”的 CPU 容量。因此,过载的检测不仅考虑 CFS 任务使用的容量,还考虑其他调度类和 IRQ 使用的容量。

注: Qcom定制了这块,即使过载情况下,也强制使用EAS。


6. EAS 的依赖项和要求

----------------------------------------

节能感知调度 (EAS) 依赖于系统 CPU 的特定硬件属性以及内核其他功能的启用。本节列出了这些依赖项,并提供了满足这些依赖项的提示。


6.1 - 非对称 CPU 拓扑

如引言所述,目前 EAS 仅支持非对称 CPU 拓扑的平台。此要求在运行时通过检查调度域构建时是否存在 SD_ASYM_CPUCAPACITY 标志来确认。

当根域中存在容量不同的 CPU 时,调度器拓扑代码会自动设置/清除此标志。CPU 的容量由特定于架构的代码通过 arch_scale_cpu_capacity() 回调函数提供。例如,arm 和 arm64 共享此回调函数的实现,该实现结合了 CPUFreq 数据和设备树来计算 CPU 的容量(更多详情请参见 drivers/base/arch_topology.c)。

因此,要在您的平台上使用 EAS,您的架构必须实现 `arch_scale_cpu_capacity()` 回调函数,并且某些 CPU 的容量必须低于其他 CPU。

请注意,EAS 与 SMP 并非本质上不兼容,但目前尚未观察到在 SMP 平台上实现显著的性能节省。如果未来有相反的证据,此限制可能会被修改。


6.2 - 能量模型

EAS 使用平台的能量模型 (EM) 来评估调度决策对能耗的影响。因此,您的平台必须向 EM 框架提供电力成本表(power cost tables),EAS 才能启动。为此,请参阅 Documentation/power/energy-model.rst 中独立 EM 框架的文档。

另请注意,注册 EM 后,需要重新构建调度域才能启动 EAS


6.3 - 能量模型复杂度

任务唤醒路径对延迟非常敏感。当平台的能量模型过于复杂(CPU 过多、性能域过多、性能状态过多等)时,将其用于唤醒路径的成本可能过高。能量感知唤醒算法的复杂度为:

C = Nd * (Nc + Ns)

其中:Nd 为性能域的数量;Nc 为 CPU 的数量;Ns 为 OPP 的总数(例如:对于两个各有 4 个 OPP 的性能域,Ns = 8)。

在构建调度域时,会在根域级别执行复杂度检查。如果根域的 C 值高于完全任意设定的 EM_MAX_COMPLEXITY 阈值(撰写本文时为 2048),则 EAS 将不会在该根域上启动。####

注: 实际读代码看,这个复杂度影响并不大,只是在获取能耗时遍历了一下:

em_pd_energy //energy_model.h
    for (i = 0; i < pd->nr_cap_states; i++) {
        cs = &pd->table[i];
        if (cs->frequency >= freq)
            break;
    }

如果您确实想使用 EAS,但平台的能量模型复杂度过高,无法在单个根域上运行,那么您只有两种选择:

1. 将系统拆分为多个独立的、较小的根域,使用独立的 CPU 集,并在每个根域上本地启用 EAS。此方案的优点是开箱即用,但缺点是无法在根域之间进行负载均衡,这可能导致整个系统负载不平衡;

2. 提交补丁以降低 EAS 唤醒算法的复杂度,使其能够在合理的时间内处理更大的能量模型。


6.4 - Schedutil 调频器

EAS 会尝试预测 CPU 在不久的将来会以哪个 OPP 运行,以便估算其能耗。为此,它假设 CPU 的 OPP 与其利用率一致。

尽管在实践中很难保证这一假设的准确性(例如,硬件可能不会执行指令),但与其他 CPUFreq 调频器不同,schedutil 至少会请求使用利用率信号计算出的频率。因此,唯一适合与 EAS 配合使用的调频器是 schedutil,因为它是唯一能够在频率请求和能耗预测之间提供一定程度一致性的调频器。

不支持将 EAS 与除 schedutil 之外的任何其他调频器一起使用


6.5 缩放不变的利用率信号

为了在所有 CPU 和所有性能状态下进行准确预测,EAS 需要频率不变且 CPU 不变的 PELT 信号。这些信号可以通过架构定义的 `arch_scale{cpu,freq}_capacity()` 回调函数获得。

不支持在未实现这两个回调函数的平台上使用 EAS。


6.6 多线程 (SMT)

EAS 目前的版本不支持 SMT,因此无法利用多线程硬件来节省能源。EAS 将线程视为独立的 CPU,这实际上可能会对性能和能耗产生负面影响。

EAS 不支持 SMT。

 

posted on 2026-03-19 15:48  Hello-World3  阅读(3)  评论(0)    收藏  举报

导航