所有省电技术,都是“占空比游戏”
从 BLE 广播间隔到 CPU 的 C-State,用一个公式解释所有低功耗设计
你可能会觉得:省电技术五花八门——蓝牙有广播间隔、连接间隔,Wi‑Fi 有 PSM 省电模式,CPU 有 C1 到 C10 各种睡眠状态,操作系统有 Tickless 内核……它们之间有没有共同的本质?
有。它们都是同一个数学公式的不同变体:
P_avg = P_active × D + P_sleep × (1 - D)
其中 D = T_active / (T_active + T_sleep),就是占空比——活跃时间占总时间的比例。
你想省电?就把 D 降下来。但 D 不能无限小,因为从睡眠回到活跃需要时间,这个时间叫唤醒延迟。你愿意等越久,功耗就能降得越低。
这就是能量-延迟权衡曲线。所有低功耗设计,都是在这条曲线上找一个合适的点。
一、CPU 的 C-State:睡眠越深,代价越大
现代处理器支持多个电源状态(C-State),数字越大睡得越深,唤醒越慢。
| C-State | 硬件行为 | 唤醒延迟 | 功耗(相对 C0) |
|---|---|---|---|
| C0 | 执行指令 | – | 100% |
| C1 (HLT) | 停止时钟,缓存保留 | < 1µs | 1-5% |
| C2 | 停止内部时钟 | 几 µs | 0.5-2% |
| C3 | 关闭 PLL,可能 flush L1/L2 | 10-50 µs | 0.1-0.5% |
| C6 及更深 | 核心电压几乎为零,缓存写回 LLC | 100 µs – ms | < 0.1% |
每个 C-State 都有一个 target_residency——进入该状态后至少要停留多长时间,才能抵消进入/退出的开销。Linux 的 cpuidle governor 会预测下次唤醒的时刻,只选择那些 target_residency 小于预测空闲时长的状态。
反直觉事实:如果空闲时间太短,进入深睡再醒来的能量开销可能超过浅睡一直等的能量。所以更深的睡眠不一定更省电。
二、BLE:通过间隔参数精细控制占空比
BLE 是专为极低功耗设计的无线协议,其省电完全依赖于可编程参数。
广播模式(无连接):
- 广播间隔范围:20ms ~ 10.24s
- 平均功耗 ≈ 峰值电流 × (广播事件时长 / 间隔)
- 例子:峰值 5mA,事件 1ms,间隔 20ms → 平均 250µA;间隔 2s → 平均 2.5µA
- 代价:设备被发现的时间 ≈ 广播间隔 × 1.5,连接建立变慢。
连接模式:
- 连接间隔范围:7.5ms ~ 4s
- 从机在每个连接间隔醒来监听,无数据则立即睡去。
- 从设备延迟(Slave Latency)允许从机忽略连续 N 个连接事件而不被断连。例如连接间隔 2s,延迟 9 → 从机每 20s 才响应一次,平均功耗可降到几 µA。
- 代价:主机发起的下行数据最多可能延迟 (延迟 +1) × 连接间隔。
一个真实的传感器参数组合(续航一年):
- 未连接时:广播间隔 2s,TX 功率 -20dBm,平均电流 5µA
- 连接后空闲:连接间隔 4s,从延迟 19(每 80s 响应一次),加上 BLE 控制器的自主保活,平均电流 < 2µA
- 数据上传时:临时切换到 15ms 连接间隔,延迟降到几十 ms,仅持续几百 ms
200mAh 的 CR2032 电池可支撑 4-5 年。
三、Wi‑Fi 的省电模式(PSM)和 TWT
Wi‑Fi 功耗远高于 BLE,但通过省电模式可以大幅降低占空比。
传统 PSM:
- Station 在 Beacon 间隔(通常 100ms)醒来,检查 TIM 位是否有数据。无数据则立即睡去。
- 占空比 ≈ 唤醒时间 / 100ms,典型平均电流几 mA。
- 延迟代价:下行数据最多缓存一个 Beacon 周期。
**Wi‑Fi 6 目标唤醒时间(TWT)**:
- 设备与 AP 协商一个唤醒时刻表,比如每 1s 醒 10ms。
- 比传统 PSM 更灵活,可将平均功耗降到几百 µA,接近 BLE 水平(但吞吐量仍受限于唤醒窗口)。
硬件保活:现代 Wi‑Fi 芯片可以独立发送空数据包维持连接,无需 CPU 干预。这是典型的“将占空比下放到外设”,让 CPU 可以安心睡大觉。
四、USB 的挂起与选择性挂起
USB 是有线总线,但省电机制同样体现占空比思想。
挂起:总线空闲超过 3ms,设备自动进入挂起状态,电流 < 2.5mA(很多设备做到 200µA)。恢复时需主机发送唤醒信号或设备远程唤醒,延迟约几毫秒。
选择性挂起:OS 可以单独挂起一个空闲的 USB 设备(如未使用的鼠标),而总线上的其他设备仍然活跃。这允许 USB 控制器进入低功耗 D-State,进而释放 CPU 的睡眠约束。
痛点:某些廉价 USB 设备不支持选择性挂起,导致整个 USB 控制器无法睡眠,从而阻止 CPU 进入深度 C-State。诊断命令:lsusb -t查看设备树,powertop查看唤醒频率。
五、操作系统:Tickless 内核消灭周期性唤醒
传统 OS 内核有一个固定的时钟中断(例如 Linux HZ=1000 表示每 1ms 一次)。即使系统完全空闲,CPU 也会每 1ms 被唤醒一次,这相当于强制占空比下限至少 0.1%,而且完全无法进入深度 C-State(因为空闲时长永远小于 1ms)。
Tickless 模式(LinuxCONFIG_NO_HZ_IDLE,FreeRTOS 的 Tickless Idle Mode)解决了这个问题:当所有任务都阻塞时,内核停止周期性时钟中断,改为设置一个单次定时器,到期时间 = 下一个已知的定时器事件或外设超时。CPU 可以在此期间进入任意深度的 C-State,直到该定时器或外部中断唤醒它。
效果:唤醒频率从 1000Hz 降到下一个事件的频率(例如 0.1Hz),占空比下降几个数量级。
代价:在睡眠期间,get_tick()类函数不再递增,依赖它的超时逻辑需要特殊处理。
六、三个反直觉的启发
- 更深的睡眠不一定更省电
- 如果空闲时间太短,进入深度睡眠再醒来的能量开销(包括保存/恢复状态、重新锁定 PLL 等)可能超过浅睡一直等的能量。这叫“睡眠开销过路费”。
- 最快的计算也可能是最省电的
- 对于突发性任务(如传感器数据处理),用最高频率快速完成(race‑to‑idle),然后让 CPU 进入极深睡眠,总能耗往往低于降频慢慢跑。因为睡眠时的功耗远低于运行时的功耗。
- 最大的敌人不是功耗,是唤醒频率
- 一颗芯片深度睡眠时可能只耗 1µA,但如果每 10ms 被唤醒一次(哪怕只醒 1ms),平均功耗 ≈ 1mA×0.1 + 1µA×0.9 ≈ 100µA,比 1µA 大了 100 倍。所以合并中断、延长轮询间隔、使用硬件 offload这些减少唤醒次数的技术,往往比降低活跃功耗更有效。
七、一个可操作的思维框架
当你拿到一个带电源管理的系统,按这四步走:
- 画出唤醒时间线:记录系统从启动到下一次睡眠之间,被哪些事件唤醒(中断、定时器、DMA)。每个事件标出:唤醒时刻、活跃时长、下一次睡眠时刻。
- 计算当前占空比:D = ΣT_active / T_total。目标是把 D 压到 1% 以下(对电池供电设备)。
- 找出占空比的主要贡献者:
- 唤醒次数太多?→ 合并中断,增大轮询间隔,用硬件保活。
- 单次活跃时间太长?→ 优化代码,或用 race‑to‑idle。
- 某个外设无法睡眠?→ 检查驱动是否支持 runtime PM。
- **逐层检查“延迟预算”**:
- 从应用层往下问:用户/应用能忍受的最大延迟是多少?
- 这个延迟能否分配给下层的睡眠?
- 下层是否提供了相应深度的睡眠状态?
- 如果某层没有提供足够的睡眠深度,那就是瓶颈。
八、写在最后
下次你看到一个低功耗参数(比如 BLE 广播间隔 1s,或者 CPU 进入 C6 的驻留时间),不要再把它当作孤立的数字。把它放进占空比公式里:
P_avg = P_active × D + P_sleep × (1 - D)
问自己:当前 D 是多少?延迟预算还有多少?哪个外设在频繁唤醒系统?
当你学会用这个公式审视每一个省电技术,你就掌握了系统级能耗优化的元思维。
本文节选自《权衡之境》主题 21。书稿已完成,出版在即。
关注「权衡之境」,获取新书信息和更多技术哲学文章。
——高翔,技术哲学作者,系统架构师。著有《权衡之境:一位工程师的技术哲学笔记》,专注技术决策的底层逻辑与思维模型。
