介绍
CPU 如何节省电量?您将在本文中了解有关 CPU 电源管理的所有基础知识。
处理器/CPU 的设计目标是在特定负载下永远运行。由于我们几乎没有人会24x7持续使用 CPU 的所有资源进行计算,因此大多数时候 CPU 都无法以设计的最大速度运行。那么,让整个 CPU 保持满负荷运行的意义何在?这就是 CPU 电源管理的意义所在。电源管理主题涵盖的内容远不止这些,包括 RAM、GPU 等,但在本文中,我将仅向您介绍 CPU 方面的内容。
这篇文章中的一般信息适用于所有现代 CPU,但细节可能非常具体到单个 CPU(尤其是处理器系列),我使用的是 Intel® Xeon® E3-1245 v5 @ 3.50GHz,这是 Xeon E3–1200 v5 系列(以前称为 Skylake)处理器(型号 0x5e)。
本文主要参考了以下资料:
- Intel(R) Xeon 处理器 E3–1200 v5 产品系列数据表
- Intel(R) Xeon 处理器 E3–1245 v5 产品规格
- 软件对平台能效的影响(英特尔白皮书)
- Intel(R) 64 和 IA-32 架构软件开发人员手册
- ACPI 规范 v6.2
- 维基百科上的 ACPI 页面
- Linux 内核源代码版本 4.13.0
CPU 功能
在其官方产品页面上,我的 CPU 列出了以下特点:
- 空闲状态
- 增强型 Intel SpeedStep® 技术
您可以在页面上看到“空闲状态 (C 状态) 用于在处理器空闲时节省电量。”和“Intel SpeedStep® 技术根据处理器负载在高低电平之间同时切换电压和频率。”
您将在本文中了解这两种描述的含义。
如何降低CPU运行时的功耗?
在生产 CPU 上(因此不考虑在设计 CPU 时可以做的事情),为了节省电量,您可以做两件事。您可以:
- 通过完全关闭子系统(核心或其他资源,如时钟或缓存)的电源(从而降低电压,将其降至零)来消除子系统的功耗
或者
- 通过降低子系统和/或整个处理器的电压和/或频率来降低功耗
第一个很容易理解,如果你关闭电源,就不会消耗任何电量。
第二点需要多解释一下。集成电路(例如处理器)的功耗与频率成线性比例,与电压成二次比例。
P ~ fV²
对于那些对数字电子产品了解更多的人来说请注意:
Pcpu其实就是=Pdynamic+Psc+Pleak
在活动的 CPU 上,Pdynamic 是最重要的部分,它是我在这里提到的部分,与频率成正比,与电压成二次方。
Psc=Pshort-circuit与频率成正比。
漏极电压与电压成正比。
而且,电压和频率并不是独立的,似乎呈线性关系。(async.org.uk/tech-report…)
更高的(串行)性能需要更高的频率和更高的电压,因此对功耗的影响更大。
CPU 消耗的最大功率是多少?
这在很大程度上取决于处理器,但 E3–1245 v5 @ 3.50 Ghz 的热设计功耗 (TDP) 为 80 瓦。这是 CPU 可以永久维持的平均值(下图中的功率限制/PL1),通常也是冷却解决方案应设计为可靠性的值。CPU 消耗的实际功率可能会暂时更高(如下图所示为 PL2、PL3 和 PL4)。TDP 是在高复杂度(~最坏情况)负载下测量的,所有核心均以基本频率(3.50 Ghz)运行。

CPU 封装电源控制
您可以在上图中看到,CPU 在 PL2 下消耗的功率最多可以超过 TDP 达 100 秒,这实际上是一个相当长的持续时间。
处理器功率状态(C 状态)与性能状态(P 状态)
降低处理器功耗的两种方法:
- 关闭子系统
- 电压/频率降低
分别通过使用以下方法实现:
- C 状态
- P 状态
C 状态描述的是第一种情况,因此它们是空闲(省电)状态。为了关闭子系统,该子系统不应运行任何程序,因此它应处于空闲状态,不执行任何操作,也不执行任何操作。因此,C 状态 x(Cx)表示 CPU 的一个或多个子系统处于空闲状态,即关闭状态。
另一方面,P 状态描述的是第二种情况,因此它们是执行(省电)状态。子系统实际上正在运行,但不需要完全发挥性能,因此其运行的电压和/或频率会降低。因此,P 状态 x,即 Px,表示它所指的子系统(例如 CPU 核心)正在以特定的(频率、电压)对运行。
由于大多数现代 CPU 在单个封装中都有多个内核,因此 C 状态进一步分为内核 C 状态(CC 状态)和封装 C 状态(PC 状态)。存在 PC 状态的原因是处理器中还有其他(共享)组件,在使用它们的所有内核关闭后,这些组件也可以关闭(例如共享缓存)。但是,作为用户或程序员,我们无法控制这些组件,因为我们不直接与封装交互,而是与各个内核交互。因此,我们只能直接影响 CC 状态,而 PC 状态则根据内核的 CC 状态受到间接影响。
状态从零开始编号,如 C0、C1… 和 P0、P1… 数字越高,节省的电量越多。C0 表示不通过关闭某些设备来节省电量,因此所有设备都处于开启状态。P0 表示最大性能,因此使用的最大频率、电压和功率。
C 状态
基本 C 状态(由 ACPI 定义)包括:
- C0:活动,CPU/核心正在执行指令。P 状态与此相关,CPU/核心可能以最大性能运行(因此处于 P0)或以较低的性能/功率运行(因此处于 P0 以外的任何状态)。
- C1:暂停,不执行任何操作,但可以立即返回 C0。由于它不工作(但已暂停),因此 P 状态与 C1 或除 C0 之外的任何 Cx 无关。
- C2:停止时钟,与 C1 类似,但返回到 C0 需要更长的时间。
- C3:睡眠。它可以返回到 C0,但需要更长的时间。
许多现代 CPU 具有更多 C 状态,根据其数据表,Intel® Xeon® E3-1200 v5 系列具有 C0、C1、C1E(C1 增强型)、C2、C3、C6、C7 和 C8。C1 和 C1E 仅为 CC 状态,而 C2 仅为 PC 状态。所有其他状态均为 CC 状态和 PC 状态。
注意:由于 Intel 超线程,因此还存在线程级 C 状态。但是,单个线程只能请求 C 状态,但只有当核心进入该 C 状态时才会发生省电操作。我一般不会在这篇文章中讨论线程 C 状态和超线程。
数据表中对这些状态的描述是:

处理器 IA 核心/封装状态支持表
注:上面的LLC指的是Last Level Cache,也就是处理器中的共享的L3缓存。
这里有一个直观的描述:

灵活的 C 状态可选择空闲功率水平与响应能力(从软件影响到平台能效白皮书)
使用 C 状态省电的一个非常基本的时间表是:
- 正常运行在C0。
- 首先,空闲核心的时钟停止。(C1)
- 然后,刷新核心的本地缓存(L1/L2)并关闭核心电源。(C3)
- 然后,当所有核心都断电时,包的共享缓存 (L3/LLC) 将被刷新,最后包/整个 CPU 可以 (几乎) 断电。我说几乎是因为我猜必须有某个东西通电才能返回到 C0。
您可能已经猜到了,CC 状态和 PC 状态并不是独立的,因此有些组合是不可能的。下图总结了这一点:

Intel Xeon E3–1200 v5 产品系列的处理器封装和 IA 核心 C 状态
显然,如果某个核心正在运行(C0),则该包不能处于 C0 以外的状态。另一方面,如果某个核心完全断电(C8),则只要任何其他核心仍在运行,该包仍可以处于 C0。
注意:英特尔软件开发人员手册提到了子 C 状态或子状态,这意味着 C 状态(类型)实际上包含一个或多个子 C 状态。在检查 Linux 内核中的 intel_idle 驱动程序代码后,我了解到例如 C1 和 C1E 具有相同的状态类型 C1,但它们是子类型 0 和 1。
CPUID 指令可以发现 C1..C8 的八个 C 状态类型 (0..7) 的子类型数量。查看我的 CPU,cpuid 程序输出返回:
MONITOR/MWAIT (5):
smallest monitor-line size (bytes) = 0x40 (64)
largest monitor-line size (bytes) = 0x40 (64)
enum of Monitor-MWAIT exts supported = true
supports intrs as break-event for MWAIT = true
number of C0 sub C-states using MWAIT = 0x0 (0)
number of C1 sub C-states using MWAIT = 0x2 (2)
number of C2 sub C-states using MWAIT = 0x1 (1)
number of C3 sub C-states using MWAIT = 0x2 (2)
number of C4 sub C-states using MWAIT = 0x4 (4)
number of C5 sub C-states using MWAIT = 0x1 (1)
number of C6 sub C-states using MWAIT = 0x0 (0)
number of C7 sub C-states using MWAIT = 0x0 (0)
还请注意英特尔手册:“MWAIT 扩展的 C0 到 C7 状态的定义是特定于处理器的 C 状态,而不是 ACPI C 状态”。因此,不要将它们与我之前提到的 ACPI C 状态混淆,它们显然是相关的,并且之间存在映射,但它们并不完全相同。
我已经根据我的 CPU(型号 0x5e)的驱动程序源代码创建了下面的图表intel_idle。横轴标签表示 C 状态名称:特定于处理器的状态:特定于处理器的子状态,纵轴表示驱动程序源代码中的退出延迟和目标驻留值。exit_latency 用作评估此状态的实时延迟影响的指导(例如,从此状态返回到 C0 需要多长时间),目标驻留表示核心必须停留在此状态以克服进入/退出功率成本的最短时间。注意纵轴上的对数刻度,更深状态的延迟和盈亏平衡功率影响呈指数级增长。

intel_idle 驱动程序源代码中的 C 状态退出延迟和目标驻留常量
注意:虽然表中有 C9 和 C10,但由于它们有 0 个子状态,因此在我的 CPU 中未使用它们。同一系列中的其他 CPU 也可能支持这些状态。
ACPI 电源状态
在介绍 P 状态之前,最好先介绍一下 ACPI 电源状态。作为用户,我们在使用计算机时通常知道这些状态。所谓的全局系统状态 (Gx 状态) 如下所列:

全局电源状态摘要(来自 ACPI 规范 v6.2)
还有一种特殊的全局状态 G1/S4,即非易失性睡眠,其中系统状态保存到非易失性存储器(如磁盘)然后关闭。这使得可以像软关机状态一样消耗最少的电量,但无需重启即可从此状态返回 G0。这就是我们所知的休眠或挂起到磁盘。
然后是睡眠状态(Sx 状态)。包括 S0(无睡眠状态),共有 6 种睡眠状态。S1-S4 与 G1 一起使用,S5 是与 G2 一起使用的 Soft Off 睡眠状态。简单总结一下:
- GO/S0:计算机正在运行,而不是休眠状态。
- G1:睡觉
- G1/S1:挂起时通电。系统状态被保留,因此 CPU 和 CPU 缓存仍然通电。
- G1/S2:CPU 断电。因此 CPU 和 CPU 缓存丢失。
- G1/S3:待机、睡眠或挂起到 RAM (STR)。系统 RAM 保持通电状态。
- G1/S4:休眠或挂起到磁盘。所有内容都保存到非易失性存储器(如磁盘),因此 RAM 和可能整个系统都处于关闭状态。
- G2/S5:软关机。与机械关机类似,但可以将计算机从省电模式唤醒的装置功率最小。不保存任何状态,因此需要重新启动才能返回 G0。
- G3:机械关闭。电源单元断开连接(例如通过电源开关)。只有实时时钟 (RTC) 之类的东西在运行,因为它们有自己的小电池。显然没有保存任何状态,因此需要重新启动才能返回到 G0。
对于我的 CPU,请参见下图,上面提到的所有 C 状态都处于 ACPI G0/S0。基本上,它说的是,一旦进入任何睡眠模式 (G1),CPU(封装)就会断电。

Intel Xeon 处理器 E3-1200 v5 产品系列的处理器功率状态
因此支持的 ACPI 状态是:

Intel Xeon 处理器 E3–1200 v5 产品系列支持的 ACPI 系统状态
ACPI G/S 状态和 CPU C 状态组合
很高兴在表格中看到所有这些组合:

Intel Xeon 处理器 E3–1200 v5 产品系列的 G、S 和 C 接口状态组合
在 G0/S0/C8 中,只有处理器包通电,但所有核心都断电。
在 G1(S3 或 S4)中,没有(有效的)C 状态(无 CC 无 PC),因为 CPU 已完全关闭。
对于 G3,没有 S 状态。系统不是处于休眠状态,而是机械断电,无法唤醒,必须先开机。
如何以编程方式请求更低功耗的 C 状态?
请求低功耗状态(从而将状态从 C0 更改为其他状态)的现代(但不是唯一)方法是使用 MWAIT 或 HLT 指令。这些是特权指令,用户程序无法执行它们。
MWAIT(监控等待)指示处理器在等待对指定地址范围(由另一条指令 MONITOR 设置)的写入/写入时进入优化状态(C 状态)。对于电源管理,MWAIT 与 EAX 一起使用,EAX[7:4] 位指示目标 C 状态,EAX[3:0] 指示子 C 状态。
注意:我相信目前只有 AMD 有这个功能,但也有 MONITORX/MWAITX 指令,除了监控地址范围的写入之外,还检查计时器的到期时间。这也称为定时 MWAIT。
HLT(Halt)指令停止执行,内核进入 HALT 状态,直到发生中断。这意味着内核切换到 C1 或 C1E 状态。
什么会触发 CPU 核心进入 Cx 状态?
一个简单的答案是:
- 当发生中断或 MWAIT 指令监视的地址上发生写入事件时,在启动时进入 C0。
- C1/C1E 与 HLT 或 MWAIT 指令一起输入。
- 使用 MWAIT 指令进入 C3,然后 L1 和 L2 缓存刷新到 LLC,所有核心时钟停止。但是,核心保持其状态,因为它仍然通电。
- 使用 MWAIT 指令进入 C6,然后将处理器状态保存到专用 SRAM 中,并将核心电压降至零。在此状态下,核心没有电源。退出 C6 时,处理器状态从 SRAM 恢复。
- 对于核心来说,C7 和 C8 与 C6 相同。
再次提醒一下,我在这个答案中省略了超线程。
正如我之前在图表中展示的那样,深度 C 状态的转换具有更高的延迟和能源成本。因此,这种转换应该谨慎进行,尤其是在电池供电的设备上。
是否可以禁用 C 状态(因此始终使用 C0)?
这是可能的,但不建议。数据表(第 4.2.2 节第 64 页)中有这样一条注释:“除非启用所有低功耗空闲状态,否则无法保证长期可靠性”。因此您真的不应该禁用 C 状态。
中断如何影响处于睡眠状态的处理器/核心?
当触发中断时,必须唤醒相应的核心并将其置于 C0 状态。但是,例如,Intel Xeon E3-1200 v5 具有一项称为“电源感知中断路由 (PAIR)”的功能,它有两个好处:
- 为了节省电量,可以将中断路由到活动核心,以免唤醒空闲核心
- 为了提高性能,可以将中断路由到空闲 (C1) 核心,而不是已经 (高度) 工作的核心
P 状态
P 状态意味着 CPU 核心也处于 C0 状态,因为它必须通电才能执行代码。P 状态基本上允许改变 CPU 核心的电压和频率(换句话说,工作点)以降低功耗。有一组 P 状态对应于不同的工作点(电压-频率对),而 P 状态指的是一个这样的工作点。最高(频率和电压)工作点是最高性能状态,即 P0。
可以在 OS/操作系统控制(Intel SpeedStep® 技术)或硬件控制(Intel® Speed Shift 技术)模式下使用 Intel Xeon E3–1200 v5 中的 P 状态。以下有关 P 状态的信息特定于 Intel Xeon E3–1200 v5 系列,但我猜其他现代处理器也一样或类似。
操作系统控制的 P 状态
在这种方法中,OS 知道 P 状态,并且 OS 会请求特定的 P 状态。这基本上意味着选择工作频率,而电压则由处理器根据频率和其他因素自动选择。通过将 P 状态写入特定于模型的寄存器(即向 IA32_PERF_CTL 写入 16 位值)来请求 P 状态后,电压将转换为自动计算的值,并且时钟发生器 (PLL) 将锁定到请求的频率。所有核心共享相同的 P 状态,因此无法为核心单独设置它。可以从另一个特定于模型的寄存器(IA32_PERF_STATUS)读取当前 P 状态/工作点。
P 状态转换非常快,因此每秒可以进行多次转换。这与 C 状态转换有很大不同,C 状态转换具有更高的延迟和功耗成本。
HW 控制的 P 状态
在这种方法中,OS 知道硬件对控制 P 状态的支持,并发出仅指定工作负载要求的请求,不请求特定的 P 状态或频率。根据 OS 给出的提示以及许多其他因素和限制,硬件会选择一个 P 状态。
我将在另一篇文章中对此进行更详细的介绍,但只是为了让您有个概念,我当前的 Linux 桌面以这种模式运行,我通过检查 IA32_PM_ENABLE 了解这一点,最高(非保证)性能级别为 39,最低性能级别为 1。这大致意味着有 39 种不同的 P 状态。目前,操作系统请求 39 作为最低和最高性能,并且优先考虑性能,这些都是因为我已禁用内核中的动态 CPU 频率变化。
关于英特尔® Turbo Boost 的说明
由于 TDP(热设计功率)是 CPU 可以承受的最大功率,当功耗低于此值且在某些其他条件下时,CPU 频率可以超过基本频率(此 CPU 从 3.50 GHz 增加到 3.90 GHz),因为这种条件不会使功耗超过 TDP。Turbo Boost 还可以在短时间内暂时将功耗增加到 PL2/Power Limit 2。可以通过向硬件提供提示来修改 Turbo Boost 的行为。
上述有关 C 状态和 P 状态的信息是否适用于移动/笔记本电脑处理器或嵌入式/手机处理器?
例如,最近的 MacBook Air 处理器 i5-5350U 主要具有我上面描述的所有功能(我不确定硬件控制的 P 状态)。我还查看了 ARM Cortex-A 文档,虽然术语不同,但看起来它的电源控制机制非常相似。
所有这些实际上是如何工作的呢,例如在 Linux 中?
我们将在另一篇文章中回答这个问题,敬请关注。
我如何监控我的 CPU?
并不是很多应用程序都可以看到这些信息,但是你可以使用例如CoreFreq。
这里显示系统信息。(以下是所有输出的一部分)
$ ./corefreq-cli -s
Processor [Intel(R) Xeon(R) CPU E3-1245 v5 @ 3.50GHz]
|- Architecture [Skylake/S]
|- Vendor ID [GenuineIntel]
|- Microcode [ 198]
|- Signature [ 06_5E]
|- Stepping [ 3]
|- Online CPU [ 4/4 ]
|- Base Clock [100.12]
|- Frequency (MHz) Ratio
Min 800.94 [ 8 ]
Max 3504.10 [ 35 ]
|- Factory [100.00]
3500 [ 35 ]
|- Turbo Boost [UNLOCK]
1C 3904.57 < 39 >
2C 3804.45 < 38 >
3C 3704.33 < 37 >
4C 3604.22 < 36 >
|- Uncore [UNLOCK]
Min 800.94 < 8 >
Max 3904.57 < 39 >
...
Technologies:
|- System Management Mode SMM-Dual [ ON]
|- Hyper-Threading HTT [OFF]
|- SpeedStep EIST < ON>
|- Dynamic Acceleration IDA [ ON]
|- Turbo Boost TURBO < ON>
|- Virtualization VMX [ ON]
|- I/O MMU VT-d [OFF]
|- Hypervisor [OFF]
Performance Monitoring:
|- Version PM [ 4]
|- Counters: General Fixed
| 8 x 48 bits 3 x 48 bits
|- Enhanced Halt State C1E <OFF>
|- C1 Auto Demotion C1A < ON>
|- C3 Auto Demotion C3A < ON>
|- C1 UnDemotion C1U < ON>
|- C3 UnDemotion C3U < ON>
|- Frequency ID control FID [OFF]
|- Voltage ID control VID [OFF]
|- P-State Hardware Coordination Feedback MPERF/APERF [ ON]
|- Hardware-Controlled Performance States HWP [ ON]
|- Hardware Duty Cycling HDC [ ON]
|- Package C-State
|- Configuration Control CONFIG [ LOCK]
|- Lowest C-State LIMIT [ 0]
|- I/O MWAIT Redirection IOMWAIT [Disable]
|- Max C-State Inclusion RANGE [ 0]
|- MWAIT States: C0 C1 C2 C3 C4 C5 C6 C7
| 0 2 1 2 4 1 0 0
|- Core Cycles [Present]
|- Instructions Retired [Present]
|- Reference Cycles [Present]
|- Last Level Cache References [Present]
|- Last Level Cache Misses [Present]
|- Branch Instructions Retired [Present]
|- Branch Mispredicts Retired [Present]
Power & Thermal Monitoring:
|- Clock Modulation ODCM <Disable>
|- DutyCycle < 6.25%>
|- Power Management PWR MGMT [ LOCK]
|- Energy Policy Bias Hint [ 0]
|- Junction Temperature TjMax [ 0:100]
|- Digital Thermal Sensor DTS [Present]
|- Power Limit Notification PLN [Present]
|- Package Thermal Management PTM [Present]
|- Thermal Monitor 1 TM1|TTP [ Enable]
|- Thermal Monitor 2 TM2|HTC [Present]
|- Units
|- Power watt [ 0.125000000]
|- Energy joule [ 0.000061035]
|- Window second [ 0.000976562]
这里显示有关内核的信息,包括空闲驱动程序:(以下是所有输出的一部分)
$ ./corefreq-cli -k
Linux:
|- Release [4.15.0-45-generic]
|- Version [#48-Ubuntu SMP Tue Jan 29 16:28:13 UTC 2019]
|- Machine [x86_64]
...
Idle driver [@intel_idle]
|- State: POLL C1 C1E C3 C6 C7s C8
|- Power: -1 0 0 0 0 0 0
|- Latency: 0 2 10 70 85 124 200
|- Residency: 0 2 20 100 200 800 800
这里监控包:
$ ./corefreq-cli -g
Cycles State(%)
PC02 1121802850 32.49
PC03 1298328500 37.83
PC06 0 0.00
PC07 0 0.00
PC08 0 0.00
PC09 0 0.00
PC10 0 0.00
PTSC 3503877892
UNCORE 150231
这里监控(核心)C 状态的计数器:
$ ./corefreq-cli -c
CPU Freq(MHz) Ratio Turbo C0(%) C1(%) C3(%) C6(%) C7(%) Min TMP:TS Max
#00 355.67 ( 3.55) 10.15 10.28 26.43 0.04 11.49 51.77 41 / 45:55 / 56
#01 355.64 ( 3.55) 10.15 10.38 19.21 0.68 15.44 54.28 42 / 45:55 / 55
#02 389.95 ( 3.89) 11.13 11.35 15.67 0.16 18.17 54.65 40 / 43:57 / 54
#03 365.38 ( 3.65) 10.43 10.61 19.77 0.18 13.93 55.51 40 / 43:57 / 54
Averages: Turbo C0(%) C1(%) C3(%) C6(%) C7(%) TjMax: Pkg:
10.46 10.66 20.27 0.27 14.76 54.05 100 C 46 C
这里监测功率和电压:
$ ./corefreq-cli -V
CPU Freq(MHz) VID Vcore
#00 130.70 0 0.0000
#01 120.08 0 0.0000
#02 124.18 0 0.0000
#03 103.46 9784 1.1943
Package Cores Uncore Memory
Energy(J): 13.415222168 2.248596191 0.000000000 0.951416016
Power(W) : 26.830444336 4.497192383 0.000000000 1.902832031