@[toc]
EWMA 指数加权移动平均
EWMA 是什么
EWMA(Exponential Weighted Moving Average,指数加权移动平均)是一种递推式平滑方法:用一个“带记忆”的平滑值表示最近一段时间的趋势,同时让更久远的数据以指数形式衰减,从而降低短期抖动对决策的影响。
典型形式为:
$$
[
S_t = \alpha \cdot x_t + (1-\alpha)\cdot S_{t-1}
]
$$
- $(x_t)$:当前观测样本
- $(S_t)$:平滑后的状态值
- $(\alpha\in(0,1])$:平滑系数(越大越“敏感”,越小越“稳”)
将递推展开后,历史样本的权重会按 $((1-\alpha)^k)$ 逐步衰减,因此称为“指数加权”。
EWMA 的核心原理
只保存一个状态值,持续吸收新样本
EWMA 不需要保存历史窗口,只需要维护一个状态 (S)。每到一个新样本,就把旧状态衰减一部分,再注入一部分新样本权重:
- 旧状态贡献:$((1-\alpha)\cdot S_{t-1})$
- 新样本贡献:$(\alpha\cdot x_t)$
这使得计算成本为 O(1),非常适合内核、驱动、实时系统等对开销敏感的环境。
平滑带来的必然代价:滞后
EWMA 的平滑本质是低通滤波,会产生“惯性”:
- 抖动会被压平(稳定性提升)
- 变化会被延迟反映(响应速度下降)
因此 EWMA 适合用于“趋势判断”,不适合用于“必须立刻跟随变化的硬性控制”。
EWMA 的作用
1) 抗抖动:避免策略在临界点频繁翻转
将 0/1 或噪声较大的观测信号转为连续平滑值,可避免“瞬时状态”导致的频繁切换(thrashing)。
2) 形成“近期趋势”的可量化指标
EWMA 能表达“近期忙/近期热/近期拥塞”的程度,而不仅是一次性的瞬时判定。
3) 低成本在线计算
无须维护队列、窗口或历史数组,只维护一个状态值即可长期运行。
适用场景
适合使用 EWMA 的典型场景
- 资源忙碌度/拥塞度估计:队列是否持续忙、丢包/重试是否持续发生
- 动态阈值与速率控制:根据平滑后的压力信号调节批量大小、并发度、派发策略
- 性能指标平滑展示:吞吐、延迟、负载等监控指标需要稳定可读
- 噪声较大但趋势重要的信号:短期尖峰不应触发策略翻转
不适用场景
不建议使用 EWMA 的场景
必须对突变立即响应
EWMA 存在滞后,突发故障/突发饥饿/硬实时截止期场景更适合“瞬时触发 + 保护机制”的方式。样本非均匀时间间隔,但公式未做时间归一化
EWMA 默认每次更新代表同等时间步长;若更新频率变化很大,需要引入时间因子(time-decay),否则平滑语义会漂移。需要严格边界或精确统计量
EWMA 是估计器而不是精确统计:无法替代 P99、直方图、滑动窗口计数等需要“真实分布”的场景。信号本身已经稳定且无噪声
平滑会引入不必要的延迟,收益很低。
以 Linux 块层 blk_mq_update_dispatch_busy 为例
blk_mq_update_dispatch_busy 派发忙碌度的 EWMA 平滑更新
目标:用平滑值表达硬件队列“近期是否持续忙”
该逻辑维护 hctx->dispatch_busy 作为“近期忙碌度”的平滑指标,用于派发策略判断(例如:忙时倾向保守派发、减少一次性出队数量;不忙时恢复批量派发以追求吞吐)。
核心设计点:
- 忙时注入样本:当本轮观察到“资源紧张/派发受限”等忙碌迹象时注入样本
- 不忙时只衰减:当本轮不忙时不注入,只有指数衰减,使忙碌痕迹自然消退
- 固定点缩放:避免整数运算导致小值过早被舍入为 0
算法对应的 EWMA 形式
示例中权重定义:
WEIGHT = 8FACTOR = 4- 忙样本注入值
sample = busy ? (1<<FACTOR) : 0
更新公式等价于:
$$
[
ewma_{new}=\frac{(WEIGHT-1)\cdot ewma_{old} + sample}{WEIGHT}
]
$$
展开后可写为:
$$
[
ewma_{new}=\frac{7}{8}\cdot ewma_{old} + \frac{1}{8}\cdot sample
]
$$
因此该实现相当于:
- $(\alpha = 1/8 = 0.125)$
- 旧值保留 (7/8),每次更新指数衰减
FACTOR(缩放因子)的意义
在整数域做 EWMA 时,如果样本只有 0/1:
- 注入项是 (\alpha\cdot 1 = 1/8)
- 整数除法会把
1/8截断为0 - 平滑值会“上不去”或“很快归零”
通过 sample = 1<<FACTOR 放大样本,使平滑值在整数域保持足够分辨率:
1<<4 = 16- 长期持续忙时
dispatch_busy会逐步收敛到接近 16 - 轻微忙碌也能留下可衰减的痕迹
低开销优化:避免无意义写回
这段逻辑:
1 | if (!ewma && !busy) |
含义是:
- 已经为 0 且本轮也不忙 → 不更新、不写回
- 降低低负载情况下的写热点与无意义触碰
示例代码:blk_mq_update_dispatch_busy
1 |
|
从该例总结 EWMA 的工程价值
- 观测信号:本轮是否出现“忙”的迹象(0/1)
- 状态变量:
dispatch_busy(连续平滑值) - 决策用途:把“偶发忙”与“持续忙”区分开,避免派发策略跟着单次抖动频繁翻转
- 实现特征:固定点 EWMA、低开销、无需历史窗口、自然衰减
实战建议:选择 EWMA 参数的经验
WEIGHT越大($(\alpha=1/WEIGHT)$ 越小)→ 更平滑、更迟钝、记忆更长WEIGHT越小 → 更敏感、更易抖动FACTOR用于保证整数分辨率,不改变“指数衰减”的结构,只改变量纲精度
调整 EWMA 参数以平衡“敏感”和“稳定”
用 $(\alpha)$ 或 WEIGHT 控制记忆长度
EWMA 基本形式:
$$
[
S_t=\alpha\cdot x_t+(1-\alpha)\cdot S_{t-1}
]
$$
工程实现常写成:
$$
[
S_{new}=\frac{(WEIGHT-1)\cdot S_{old}+sample}{WEIGHT}
\quad\Rightarrow\quad
\alpha=\frac{1}{WEIGHT}
]
$$
参数含义:
- $(\alpha)$ 越大(
WEIGHT越小):更敏感、更快响应、更容易抖动 - $(\alpha)$ 越小(
WEIGHT越大):更稳定、更慢响应、滞后更明显
用“半衰期”直观选参
历史权重按 $((1-\alpha)^k)$ 衰减。定义“半衰期步数” $(k_{1/2})$:权重衰减到 50% 所需更新次数:
$$
[
(1-\alpha)^{k_{1/2}}=0.5
\Rightarrow
k_{1/2}=\frac{\ln(0.5)}{\ln(1-\alpha)}
]
$$
用法:
- 目标:希望“约 $(k_{1/2})$ 次更新后,旧影响减半”
- 选参:按上式反推 $(\alpha)$,再映射到可实现的
WEIGHT
经验替代指标(粗略但好用):
- 记忆长度近似 $(\approx \frac{1}{\alpha})$ 个更新步
WEIGHT取 4/8/16/32 往往便于整数实现与调参
用 FACTOR(缩放)保证整数分辨率
当样本是 0/1 或小整数时,直接做整数 EWMA 容易被截断为 0。固定点缩放做法:
sample = busy ? (1<<FACTOR) : 0S始终在“放大后的量纲”上滚动
关键点:
FACTOR只改变数值量纲与分辨率,不改变指数衰减结构- 二值样本持续为 1 时,稳态 $(S)$ 会收敛到
1<<FACTOR附近(同量纲)
选参要点:
- 分辨率:
FACTOR越大,低负载下可见的“痕迹”越多 - 溢出:确保中间项
(WEIGHT-1)*S + sample不超过整数类型上限 - 阈值:若策略阈值以
S判断,阈值也应使用同量纲(例如threshold = 8对应FACTOR=4时的 0.5 程度)
用“更新频率”校准参数(避免语义漂移)
若采样每 $(\Delta t)$ 秒更新一次,WEIGHT 实际对应“多少秒的记忆”。同一组 WEIGHT 在不同更新频率下语义不同。
做法:
- 先确定目标记忆时间 $(T)$(例如希望保留约 2 秒趋势)
- 再按更新频率换算步数 $(N=T/\Delta t)$
- 再选 $(\alpha \approx 1/N)$(或用半衰期公式更精确)
在非均匀采样间隔下引入时间因子
EWMA 默认每次更新代表同等时间步长。若更新间隔不稳定,需要把“衰减”改为按真实时间衰减(time-decay)。
用时间常数 $(\tau)$ 定义连续时间 EWMA
设两次更新间隔为 $(\Delta t)$,时间衰减系数:
$$
[
decay = e^{-\Delta t/\tau}
]
$$
更新公式:
$$
[
S_t = decay\cdot S_{t-1} + (1-decay)\cdot x_t
]
$$
等价于“动态 $(\alpha)$”:
$$
[
\alpha_t = 1 - e^{-\Delta t/\tau}
]
$$
解释:
- $(\tau)$ 越大:记忆越长、响应越慢
- $(\Delta t)$ 越大:本次更新注入权重越大(因为旧值已经衰减更多)
可直接落地的伪代码
1 | // S: fixed-point state |
实现建议(整数域):
exp()用查表(按 dt 分桶)或近似(小 dt 时 $(1 - dt/\tau))$decay与S都用固定点(例如 Q16.16),避免浮点依赖dt过大时可做上限裁剪,避免一次更新“完全重置”语义不符合预期
多级分层的三种常见做法
“多级分层”在工程上常指三类结构:按对象层级分层、按时间尺度分层、按滤波级联分层。
1) 按对象层级分层:局部 EWMA → 汇总 EWMA
用于多队列/多通道/多资源对象的趋势判断:
- Level 1:每个子对象维护一个局部 $(S_i)$
- Level 2:上层对象维护汇总 $(S_{agg})$,输入来自 $({S_i})$ 的聚合值(均值、最大值、加权均值等)
示例结构(概念):
- 子队列忙碌度 $(S_i)$:用于局部派发策略
- 设备级忙碌度$(S_{dev})$:用于全局限速/整体并发调节
注意点:
- 上层输入建议是“已经平滑过的量”(例如 $(S_i)$ 的均值),减少噪声放大
- 聚合函数的选择决定偏好:
max更偏向保护最忙分支,mean更偏向整体吞吐
2) 按时间尺度分层:快慢两条 EWMA 同时维护
用于同时兼顾“突发检测”和“长期基线”:
$$
[
S^{fast}t=\alpha_f x_t+(1-\alpha_f)S^{fast}{t-1}
]
[
S^{slow}t=\alpha_s x_t+(1-\alpha_s)S^{slow}{t-1}
\quad (\alpha_f>\alpha_s)
]
$$
常见用法:
- 趋势差:$(\Delta = S^{fast}-S^{slow})$ 表示“近期相对升温”
- 触发条件:
S_fast > S_slow + margin用于进入保护模式;恢复用更严格条件避免抖动
选参建议:
- $(\alpha_f)$ 对应“反应速度”(短记忆)
- $(\alpha_s)$ 对应“基线”(长记忆)
margin与量纲一致(固定点缩放后同样需要同步缩放)
3) 按滤波级联分层:EWMA 输出再做 EWMA
用于更强平滑、接受更大滞后:
- Stage 1:对原始噪声信号做一次 EWMA
- Stage 2:对 Stage 1 的输出再做一次 EWMA
效果:
- 抖动进一步降低
- 相位滞后增加(变化更慢反映)
适用场景:
- 展示型指标(监控曲线更“顺”)
- 非硬实时的策略(更强调稳定性)
不适用场景:
- 需要快速跟踪变化的控制环(滞后叠加风险更高)
快速落地检查清单
- 目标响应时间已确定(秒/毫秒/更新步数)
- 更新间隔是否稳定:不稳定则采用 time-decay 版本
- 样本范围是否过小:二值/小整数则启用固定点缩放
FACTOR - 是否需要兼顾突发与基线:采用快慢双 EWMA
- 是否存在多对象:采用“对象层级分层”,并明确聚合函数(mean/max/weighted)







