0%

用 Little 定律解释推测解码在真实服务中的提速曲线 —— 阅读笔记

笔记日期: 2026-05-16 作者: Zhongzhu Zhou 论文: An Interpretable Latency Model for Speculative Decoding in LLM Serving 作者团队: Linghao Kong, Megan Flynn, Michael Peng, Nir Shavit, Mark Kurtz, Alexandre Marques(MIT 与 Red Hat AI) arXiv: 2605.15051v1,2026-05-14 状态: Preprint。实验栈为 GuideLLM 0.5.2 驱动 vLLM 0.13.0。


简短结论

这篇论文回答了一个我在生产环境里反复遇到的问题:为什么 batch=1 实测推测解码(SD)能跑出漂亮的加速比,一旦真实流量上来,加速就悄无声息地消失了?

到目前为止,SD 的研究路线大多是「算法侧」的:Leviathan 与 Chen 提出了 SD 的基本框架,EAGLE-1/2/3、PARD 让 draft 模型更强,Medusa、Lookahead Decoding 干脆把 draft 模型换成多头或直接不用 drafter。这些工作都在固定 batch size(通常是 1)下测量加速比。可一旦你把 draft-verify 流程塞进 vLLM 这类连续批处理(continuous batching)服务,batch size 就不再是你能控制的旋钮,而是调度器从请求流里「生成」出来的,并且和 KV-cache 压力、chunked prefill 等机制纠缠在一起,行为变得不直观。

Kong 等人提出了一个非常小的解析模型,专门处理这种情况。核心观察是:在稳定的「未饱和」区间内,请求平均延迟可以写成 roofline 风格的分解 L=C1+BC2L = C_1 + B \cdot C_2,其中 C1C_1 与负载无关,C2C_2 是每多一个并发请求带来的边际成本。利用 Little 定律消掉不可观测的有效 batch size B=RPSLB = \text{RPS} \cdot L,就得到闭式:

L=C11RPSC2.L = \frac{C_1}{1 - \text{RPS} \cdot C_2}.

理解整篇论文,这一个公式就够了。剩下的内容是把它扩展到 SD:把 C1,C2C_1, C_2 写成 prefill、verify、draft 三段成本对 EE 加权的和(EE 是每个 SD 周期期望接受的 token 数);再加上一项 MoE 校正 φ(T)=1(1m/M)T\varphi(T) = 1 - (1 - m/M)^T,刻画在不同负载下被激活的 expert 占比。

实证收益相当大。论文在 Qwen3 全系列、Llama-3.1-{8B, 70B}、gpt-oss-20b 上,在 A100 和 H100 上扫描 RPS、prefill 长度、decode 长度、接受率 α\alpha、draft 长度 kk、verifier/drafter 配对,得到:

  1. 所有非 MoE 配置归一化后,全部塌缩到同一条曲线 y=1/(1x)y = 1/(1 - x)
  2. SD 的加速形式是 Speedup=(1/C1,R)(1+(1C2,R)r/(1r))\text{Speedup} = (1/C_{1,R}) \cdot (1 + (1 - C_{2,R}) \cdot r/(1-r)),其中 r=RPSC2,Dr = \text{RPS} \cdot C_{2,D}。加速随负载变化的方向,完全由 C2,RC_{2,R} 是大于还是小于 1 决定。
  3. 在大多数现实配置里 C2,R>1C_{2,R} > 1,这给出了「SD 加速在高负载下衰减」这一现象的机制性解释。
  4. C1,RC_{1,R} 最小的 kk,往往远大于让 C2,RC_{2,R} 最小的 kk。也就是说,把 batch=1 下选好的 kk 直接搬到高吞吐量场景,几乎总是错的。
  5. MoE 模型在低负载下比 dense 预测的延迟更低(因为激活的 expert 少),随负载升高这种偏差消失。引入 φ\varphi 校正后 R2R^2 从 0.83–0.91 提升到 0.97–0.99。

我喜欢这篇论文的一点:它的贡献是「建模」,而不是「刷榜」。整个模型一屏纸放得下,拟合也很便宜(每个配置扫 9 个 RPS 点,SciPy curve_fit 一下就完事),然后可以反过来指导部署:在线估计 α\alphaRPS\text{RPS},用公式 (3) 选最优 kk。这比再来一张「在某型号 GPU 上跑了多少 tok/s」的表格要有用得多。

我会在 §6 里花一些篇幅讨论它的不足——它讲的是平均延迟、未饱和稳态,把生产里最重要的尾延迟、抢占、突发流量、排队方差都明确地排除掉了,这种局限值得放在摘要里而不是附录里。但作为「面向服务系统的 SD 一阶模型」,它是干净、好用、可复现的。


1. 前置知识

这一节面向已经上线过 LLM 推理但还没系统看过 SD 与服务排队的读者。覆盖自回归解码、prefill 与 decode 的不对称、连续批处理、roofline 分解、Little 定律、EAGLE 风格的 draft-verify 流程。

1.1 自回归解码、prefill 与 decode 的不对称

Transformer 一次只生成一个 token:第 tt 步条件在 token 1,,t11, \dots, t-1 上,得到第 tt 个 token 的分布并采样。两个阶段的执行特性差别很大:

  • Prefill:把长度为 pp 的整段 prompt 一次性并行送进模型,注意力是 O(p2)O(p^2) 但属于计算密集型,能充分利用 GPU 算力。
  • Decode:每个输出 token 顺序生成,每步只产 1 个 token 但需要把整组权重从 HBM 流到 SM,属于显存带宽密集型。这是 SD 想要绕过的瓶颈:每个 decode step 都在「读权重」,多产出几个 token 就能摊销这次读取。

记住这种不对称很关键:prefill 算术强度高,decode 算术强度低。批处理在 decode 阶段帮助最大(多个序列共享一次权重读取),但要和 KV-cache 显存压力一起权衡。

1.2 连续批处理与 chunked prefill

朴素批处理要等同 batch 里最慢的请求结束再开下一批。现代服务(Orca 提出、vLLM 推广)采用连续批处理:每次迭代调度器挑出已就绪的 decode 步、混入新到达的请求、跑一次前向。指标上看到的 batch size 是有效 batch,本身就是个随机变量。

Chunked prefill 把长 prefill 切片,避免大 prompt 阻塞 decode 队列。配合连续批处理,结果是:batch size 不再是你能调的开关,而是请求流和调度器交互的产物。论文整个动机就在于:正视这个事实,而不是假装可以人为设定 batch size。

1.3 Roofline 风格分解

Williams 等人的 roofline 模型把内核耗时分成「固定成本」(kernel launch、权重加载)和「负载相关成本」(每个元素的 FLOPs 或访存量)。把这个抽象推广到推理服务:每请求延迟里有些项与并发 batch BB 无关(比如每请求摊一次的 kernel setup、调度开销),有些项随 BB 增长(每 token 的计算量、KV-cache 显存压力)。论文写成

L=C1+BC2L = C_1 + B \cdot C_2

然后让 SciPy 把 (C1,C2)(C_1, C_2) 从数据里拟合出来。

1.4 Little 定律

Little 定律(Little 1961)是排队论里的一个恒等式:在任何稳定系统中,平均在线项数 BB 等于到达率 λ\lambda 乘以平均停留时间 LL

B=λL.B = \lambda \cdot L.

它不假设到达过程是泊松、不假设服务时间是指数,是非参数的。落到 LLM 服务上:λ=RPS\lambda = \text{RPS}(每秒请求数),LL 是每请求平均延迟(秒),BB 是平均在线请求数。把 B=RPSLB = \text{RPS} \cdot L 代回 L=C1+BC2L = C_1 + B \cdot C_2,就得到 (1) 式——不可观测的 BB 就此消失。

1.5 推测解码一段话讲完

推测解码(Leviathan 等 2023;Chen 等 2023)让一个小的 draft 模型 自回归地生成 kk 个候选 token,然后让大的 verifier 模型 把这 kk 个候选并行送一次前向。一个 rejection sampler 按 verifier 的分布接受最长的合规前缀,并提交那段;其余丢弃,重新开始一个周期。EAGLE-1/2/3(Li 等 2024–2025)把独立 drafter 换成一个轻量级的特征级 adapter,在 verifier 的隐状态上训练;论文大部分实验用的就是这种 EAGLE-3 风格 drafter。

两个关键参数:接受率 α\alpha(任一 draft token 被接受的概率,假设 i.i.d.)和 draft 长度 kk(drafter 一次猜几个)。每个周期期望接受的 token 数是

E[accepted]=1αk+11α.E[\text{accepted}] = \frac{1 - \alpha^{k+1}}{1 - \alpha}.

理想加速上界是 1+E[accepted]1 + E[\text{accepted}],但 verifier 仍要跑一次,drafter 也有自己的开销。

1.6 论文里「负载」是什么

通篇 load = 请求率 = RPS不是 GPU 利用率也不是队列长度。每个配置在两个端点之间扫:同步基线(RPS 小到每个请求都独跑)与 vLLM 即将触发抢占的最高吞吐。中间均匀取 9 个 RPS 值。饱和/抢占区被明确排除——模型在那里不适用。


2. 方法

2.1 基础延迟模型

L=C1+BC2L = C_1 + B \cdot C_2 出发。Little 定律给 B=RPSLB = \text{RPS} \cdot L。解出 LL

L(RPS)=C11RPSC2.(1)L(\text{RPS}) = \frac{C_1}{1 - \text{RPS} \cdot C_2}. \quad (1)

几条值得记的性质:

  • RPS0\text{RPS} \to 0LC1L \to C_1。纵截距就是同步(batch=1)延迟。
  • RPSC21\text{RPS} \cdot C_2 \to 1LL \to \infty,分母趋零就是饱和边界;论文只在该渐近线之下拟合。
  • 归一化后 x=RPSC2x = \text{RPS} \cdot C_2y=L/C1y = L / C_1,所有配置都塌缩到 y=1/(1x)y = 1/(1-x)

论文图 1 漂亮地展示了 dense 模型的这种塌缩。它是整篇工作最强的实证。

2.2 把推测解码塞进 C1C_1C2C_2

每个 (α,k)(\alpha, k) 单独拟合时 (1) 式仍然成立(图 2a),说明 SD 不改变延迟曲线的形状,只改变它的参数。但用一对 (C1,C2)(C_1, C_2) 同时覆盖所有 (α,k)(\alpha, k) 就拟合不动了(图 4a)。自然的修正:让 C1C_1C2C_2 显式地依赖 (α,k)(\alpha, k)

周期解剖:每个 SD 周期做 (i) 一次 verifier 前向,并行评估 k+1k+1 个 token;(ii) kk 步 drafter。prefill 每请求只做一次。定义各阶段的固定与负载相关成本 c1,p,c1,v,c1,d,c2,p,c2,v,c2,dc_{1,p}, c_{1,v}, c_{1,d}, c_{2,p}, c_{2,v}, c_{2,d}。记 gg 为 decode 长度,EE 为每周期期望接受 token 数,每请求的 SD 周期数是 g/Eg/E。所以

C1,EFF=c1,p+gE(c1,v+kc1,d),C_{1,\text{EFF}} = c_{1,p} + \frac{g}{E}\bigl(c_{1,v} + k\, c_{1,d}\bigr),

C2,EFF=c2,p+gE(c2,v+kc2,d).(3)C_{2,\text{EFF}} = c_{2,p} + \frac{g}{E}\bigl(c_{2,v} + k\, c_{2,d}\bigr). \quad (3)

注意非对称:C2,EFFC_{2,\text{EFF}} 里 verifier 项 c2,vc_{2,v} kk 缩放,只有 drafter 项随 kk 走。经验上 verifier 的负载相关项在 kk 变化下基本平坦——验证主要是把权重和 KV cache 流过 HBM,与 token 数关系不大。论文承认这个简化并给出更好的拟合。

E=(1αk+1)/(1α)E = (1 - \alpha^{k+1})/(1-\alpha) 代入闭合。此时 C1,C2C_1, C_2 通过 6 个 small coefficients c,c_{*,*} 显式依赖于 (α,k)(\alpha, k)

2.3 加速比公式与 C2,R1C_{2,R} \gtrless 1 判别

定义比值 C1,R=C1,SD/C1,DC_{1,R} = C_{1,\text{SD}} / C_{1,D}, C2,R=C2,SD/C2,DC_{2,R} = C_{2,\text{SD}} / C_{2,D}, r=RPSC2,Dr = \text{RPS} \cdot C_{2,D}。 则

Speedup=LDLSD=1C1,R(1+(1C2,R)r1r).(2)\text{Speedup} = \frac{L_D}{L_{\text{SD}}} = \frac{1}{C_{1,R}} \left( 1 + (1 - C_{2,R}) \cdot \frac{r}{1-r} \right). \quad (2)

这是真正干活的公式。三条直接推论:

  • 零负载加速 = 1/C1,R1/C_{1,R}。SD 几乎总能做到 C1,R<1C_{1,R} < 1(verifier step 变少了),所以同步加速是真的。
  • 加速随负载的斜率方向 完全由 1C2,R1 - C_{2,R} 决定:
    • C2,R<1C_{2,R} < 1:加速随负载增长。只有 α\alpha 非常高时才会出现。
    • C2,R>1C_{2,R} > 1:加速随负载衰减。常见情况。
  • 饱和处的双曲爆炸r/(1r)r/(1-r)r1r \to 1 附近发散,无论加速斜率是正是负,靠近饱和时都会被这一项主导。

这是我见过最干净的关于「SD 在 benchmark 漂亮、上线不行」现象的机制解释。它不是测量误差,也不是 drafter 的 bug,而是 SD 把成本放错位置后的结构性现象。

2.4 MoE 校正

MoE 模型在低负载下系统性地比 dense 模型预测的同步延迟更低——稀疏激活意味着每个 decode step 只触碰一部分 expert。论文用一个 expert 覆盖率公式描述这件事:

φ(T)=1(1mM)T,\varphi(T) = 1 - \left(1 - \frac{m}{M}\right)^T,

其中 mm 是每 token 选中的 expert 数,MM 是总 expert 数,TT 是有效路由 token 数。非 SD 情况下 TB=RPSLT \approx B = \text{RPS} \cdot L;SD 的 verifier 阶段 TRPSLkT \approx \text{RPS} \cdot L \cdot k,因为每次 verification 要并行处理 k+1k+1 个 token。系数因此拆成「低覆盖部分」与「饱和增量」:

L=C1,u+φC1,s1RPS(C2,u+φC2,s).(4)L = \frac{C_{1,u} + \varphi C_{1,s}}{1 - \text{RPS}(C_{2,u} + \varphi C_{2,s})}. \quad (4)

图 8 与 §4.6 给出实际拟合提升:在低负载半数据集上,R2R^2 从 0.902 → 0.997(gpt-oss-20b)、0.830 → 0.976(Qwen3-30B-A3B)、0.906 → 0.989(Qwen3-235B-A22B)。在低负载这一块的提升尤其明显,那也正是稀疏激活红利最大的区段。

2.5 实际拟合的参数

固定模型 + (prefill, decode) 后:dense-only 拟合估 (C1,C2)(C_1, C_2) 两个参数;speculation-aware 拟合估 6 个 c,c_{*,*},在所有 (α,k)(\alpha, k) 上联合;MoE-aware 拟合再加 covered/saturated 拆分与有效 TT。非线性但条件不坏,因为 E(α,k)E(\alpha, k) 在扫描里跨度足够大,能把周期级系数约束住。


3. 实验设置

  • 驱动:GuideLLM 0.5.2,在同步与吞吐天花板之间均匀扫 9 个 RPS。
  • 服务:vLLM 0.13.0,连续批处理 + chunked prefill。
  • Verifier:Llama-3.1-8B-Instruct、Llama-3.1-70B-Instruct、gpt-oss-20b、Qwen3-{0.6B, 1.7B, 8B, 14B, 30B-A3B, 32B, 235B-A22B}。
  • Drafter:多数配置用 EAGLE-3 风格轻量 drafter;Llama-3.1-70B 用 Llama-3.1-8B 做 vanilla SD,Qwen3-14B 用 Qwen3-1.7B 做 vanilla SD。
  • prompt/decode 扫描:prefill ∈ {256, 512, 768, 1024},decode ∈ {256, 512, 768, 1024};16 组组合。输入来自 Pride and Prejudice 的模拟 token 序列。
  • SD 扫描α{0.50,0.55,,1.00}\alpha \in \{0.50, 0.55, \dots, 1.00\}(覆盖 vLLM rejection sampler,强制固定 α\alpha),k{1,,10}k \in \{1, \dots, 10\}
  • 硬件:A100 SXM 单卡跑 ≤32B dense;4×A100 跑 Llama-3.1-70B;8×A100 跑 Qwen3-235B-A22B;dense Qwen3 上 H100 做交叉验证。
  • 拟合:SciPy curve_fit

一个工程意识上的注脚:强制固定 α\alpha 把测量从 drafter 质量里解耦出来——这是方法论上的正确选择,能孤立 α\alpha 的效应。但实际部署里 α\alpha 是 drafter 决定的、与上下文、prompt 风格相关,所以应用 (3) 时还需要在线量出真实 α\alpha


4. 关键结果,逐图过

4.1 图 1 —— 通用延迟塌缩

所有 dense 配置(Qwen3-{0.6B…32B} 与 Llama-3.1-{8B, 70B})归一化后塌缩到 1/(1RPSC2)1/(1-\text{RPS} \cdot C_2)。塌缩这么干净并不显然——KV-cache 压力、attention 二次项、调度器策略都可能把曲线扭出去。如此漂亮的合拢就是用 Little 定律的实证依据。

4.2 图 2 —— SD 保形状,改参数

(a) 单独拟合每个 (α,k)(\alpha, k) 时 Little 定律曲线依旧适用;(b) Qwen3-8B 在 1024/1024 下,C1,R<1C_{1,R} < 1 在所有设置下都成立(SD 降固定成本),但 C2,RC_{2,R} 通常 >1> 1,只在 α0.9\alpha \geq 0.9 才偶尔 <1< 1。图里还能看到:最小化 C1,RC_{1,R}kk 比最小化 C2,RC_{2,R}kk 大得多——这正是低负载/高负载之间的张力。

4.3 图 3 —— prefill/decode 上 C1,RC_{1,R}C2,RC_{2,R} 的最优值

对每个 (α,prefill,decode)(\alpha, \text{prefill}, \text{decode}),取在 kk 上的最佳 C1,RC_{1,R} 和最佳 C2,RC_{2,R}C1,RC_{1,R} 始终 <1< 1——SD 同步收益普遍存在;C2,RC_{2,R} 在大多数配置里仍 >1> 1,只有极高 α\alpha 的角落能 <1< 1结论:同步加速无法自动外推到高 RPS,除非 α\alpha 异常高或 kk 缩小。

4.4 图 4 —— 单参 vs 推测感知拟合

(a) 用一对 (C1,C2)(C_1, C_2) 同时拟合所有 (α,k)(\alpha, k) 拟合得很差;(b) 单独拟合得到的 (C1,C2)(C_1, C_2)(α,k)(\alpha, k) 平滑变化;(c) 切到 speculation-aware 的 (C1,EFF,C2,EFF)(C_{1,\text{EFF}}, C_{2,\text{EFF}}),又把所有 (α,k)(\alpha, k) 拉回一条线;(d) 联合拟合得到的 C,EFFC_{*,\text{EFF}} 与逐配置 (C1,C2)(C_1, C_2) 数值高度一致——参数化方式被验证。

4.5 图 5 —— 周期系数随 verifier/drafter 规模的标度

Qwen3-{0.6B, 1.7B, 8B, 14B, 32B} 上 c1,p,c1,v,c2,p,c2,vc_{1,p}, c_{1,v}, c_{2,p}, c_{2,v} 近似线性依赖 verifier 参数量;c1,d,c2,dc_{1,d}, c_{2,d} 近似线性依赖 drafter 参数量。最干净的是 c1,vc_{1,v} —— 这与「每请求开销由权重加载主导」一致。

4.6 图 6 —— 序列长度依赖

c2,pc_{2,p} 大致线性于 prefill 长度,斜率与模型有关。c2,vc_{2,v} 标度到一个有效 token 数 prefill+0.5decode\text{prefill} + 0.5 \cdot \text{decode},这正好是 decode 期间 KV cache 的时间平均大小。是个不错的机制细节。

4.7 图 7 —— leave-nn-out 外推

从 16 个 (prefill, decode) 组合里抽 n{1,2,4}n \in \{1, 2, 4\} 个做留出,剩下拟合标度趋势,再去预测留出集。较大 Qwen3 模型(≥8B)的 holdout R2R^2 几乎与全数据拟合持平:c2,pc_{2,p}c2,vc_{2,v}0.97\geq 0.97。也就是说,这些系数标度具备预测力,不只是描述性拟合。

4.8 图 8 —— MoE 校正的提升

gpt-oss-20b、Qwen3-30B-A3B、Qwen3-235B-A22B 上引入 φ\varphi 后,低 RPS 区段残差显著下降;越是小 kk(即 verifier 一次只处理很少 token、覆盖的 expert 少)提升越大。这与稀疏激活在低覆盖区域的物理含义吻合。


5. 我会真正带走的三条心得

用 Little 定律绕开 batch size 陷阱。 我读过的每篇 SD 论文之所以加速数对不上,根源是它们各自固定了不同的 batch size。在连续批处理服务里压根没有「正确」batch size。扫 RPS 拟合 L=C1/(1RPSC2)L = C_1/(1 - \text{RPS} \cdot C_2) 才是合适的评估方式——它消掉这个本来就定义不清的开关。我以后做任何服务侧评估都会借用这一招。

kk 要随负载变。 模型给了一个明确预言:高负载下应当用更小的 kk,因为 verifier 项主导而大 kk 会膨胀 C2,RC_{2,R}。这与 batch=1 SD 论文「大 kk 摊销 verifier」的建议相反。在线上估出 α\alphaRPS\text{RPS},按论文公式查最优 kk 是完全可行的策略——我觉得这是整篇最值得直接落地的部署结论。

C2,R>1C_{2,R} > 1 是工程红旗。 跑一遍 profile 拟合,若 C2,R>1C_{2,R} > 1,说明这个 SD 配置只是「同步加速」,过了某个负载阈值就会输给纯 decode。阈值可以从 (2) 解出,r=(C1,R1)/(C1,RC2,R)r^* = (C_{1,R}-1)/(C_{1,R}-C_{2,R}),把 RPS=r/C2,D\text{RPS}^* = r^*/C_{2,D} 标在监控面板上就是一条可直接看的告警线。


6. 局限与我的几点反驳

6.1 只建模平均延迟

模型瞄的是平均 LL。p95/p99 这种生产里更关心的指标基本被掠过。附录 A.5 提了一句「在大模型上 p95/p99 也大致适用」,我读到的潜台词是「能凑合,但不是为它设计的」。尾延迟主导因素是调度方差,而 Little 定律恰好把方差平均掉了。如果我的 SLO 是 p99,我不会只凭这个模型决定上不上 SD。

6.2 只在未饱和区拟合

论文显式排除吞吐天花板。但生产里最棘手的恰恰是饱和区——SLO 就是在那里被打破。论文坦诚提到这一点(「饱和处抢占导致延迟不稳,不在拟合范围内」),但这条限制应该写在摘要里而不是埋在叙述里。读者很可能误以为模型覆盖了自己的工作点。

6.3 i.i.d. 接受率假设

(3) 式里 E=(1αk+1)/(1α)E = (1 - \alpha^{k+1})/(1-\alpha) 默认 per-token 接受是 i.i.d.、α\alpha 恒定。真实 drafter 的接受率随位置衰减、被拒往往相关、并随上下文变化。论文为了内部一致强制覆盖 rejection sampler 把 α\alpha 钉住,所以模型逻辑自洽;但外推到「我这台 drafter 真实情况会怎样」时,仍需独立测一个有效 α\alpha

6.4 单卡偏置

绝大多数实验在单 A100 或 H100 上做,Llama-3.1-70B 用 4×A100,Qwen3-235B-A22B 用 8×A100,但多卡区段没有深入。Tensor parallel、pipeline parallel、尤其是 MoE 的 expert parallel 会显著改变负载相关成本结构。我希望看到 MoE 校正在 expert parallel(Tutti、DisagMoE)下被验证后再把 (5) 套到前沿 MoE 部署。

6.5 自适应 drafting 与 tree verification 未覆盖

EAGLE-2 与 dynamic lookahead 会运行时调整 kk;Medusa、SpecInfer 用候选树替代平面 kk。论文说框架能「通过额外成本项」吸纳这些变体但没有测过。2026 年部署最多的 SD 都用自适应 drafting,这是个真实缺口。

6.6 没有闭环控制

论文给的是静态 k(α,RPS)k^*(\alpha, \text{RPS}) 查表,但没有给控制器。线上 α\alphaRPS\text{RPS} 估计带噪声、有滞后;切换 kk 本身有成本(kernel 选型、KV-cache 预算都会变)。我想看一个简单控制器(滑窗估计 α,RPS\alpha, \text{RPS} → 选 kk)的稳定性分析。这是顺理成章的下一篇论文。

6.7 即便如此我仍被说服

抛开上面这些,我认为核心贡献是稳健的。图 1 的通用延迟塌缩这种实证规律很难做出来;(2) 式给「SD 加速在高负载下衰减」提供了机制性解释,把行业的手挥模糊语句钉死成可计算;MoE 的覆盖率校正是一段干净的「小物理」建模。论文短且有用,与大量 benchmark-heavy 的 SD 论文比起来是另一种气质。


7. 可复现性

  • 代码:论文未给出公开仓库——略遗憾,整套 SciPy 拟合本可以塞进一个 notebook。GuideLLM(Neural Magic)与 vLLM 是公开的。
  • 硬件:A100 SXM 与 H100 商用易得;实验是 GPU-小时 bound,而非算法复杂度 bound。
  • 方法:所有公式、扫描区间、拟合流程在正文都写清楚了。SciPy curve_fit 流程标准。认真读完后,一名研究者周末在单 A100 上重复主要拟合是可行的。
  • 注意:vLLM 版本(0.13.0)会影响 C2C_2,因为调度器启发式会改。换新版 vLLM 后数值系数会变但模型结构不变。

8. 总评

这是一篇克制、扎实的论文。它只做一件事:给推测解码在真实服务负载下的延迟一个微小的解析模型。数学不多,实证够广,运维含义明确。最大短板是适用范围——平均延迟、未饱和、i.i.d. 接受率——论文承认但低估了它的重要性。如果你在生产里跑 SD、评测 SD 方法,或者写服务系统,这篇值得读;如果只想要部署指引,那就一句话:高负载用小 kk、低负载用大 kk,先看一眼你的 C2,RC_{2,R}。我预计这篇会影响下一轮 EAGLE/PARD/Medusa benchmark 的报告范式。它值得这样的影响力。


附录 A. 推导

A.1 从 L=C1+BC2L = C_1 + B C_2 推 (1)

L=C1+(RPSL)C2    L(1RPSC2)=C1    L=C1/(1RPSC2)L = C_1 + (\text{RPS} \cdot L) C_2 \;\Rightarrow\; L(1 - \text{RPS} \cdot C_2) = C_1 \;\Rightarrow\; L = C_1/(1 - \text{RPS} \cdot C_2)。要求 RPSC2<1\text{RPS} \cdot C_2 < 1,即未饱和。

A.2 零负载加速极限

limRPS0Speedup=limr01C1,R(1+(1C2,R)r1r)=1/C1,R\lim_{\text{RPS} \to 0} \text{Speedup} = \lim_{r \to 0} \frac{1}{C_{1,R}}\bigl(1 + (1 - C_{2,R})\frac{r}{1-r}\bigr) = 1/C_{1,R}。要拿到「最好情况」的 1/C1,R1/C_{1,R},需要同步执行。

A.3 加速等于 1 的临界负载

设 Speedup = 1:1C1,R(1+(1C2,R)r1r)=1    r=C1,R1C1,RC2,R\frac{1}{C_{1,R}}\bigl(1 + (1 - C_{2,R})\frac{r}{1-r}\bigr) = 1 \;\Leftrightarrow\; r^* = \frac{C_{1,R}-1}{C_{1,R}-C_{2,R}}。当 C1,R<1<C2,RC_{1,R} < 1 < C_{2,R}r(0,1)r^* \in (0, 1),SD 加速在 RPS=r/C2,D\text{RPS}^* = r^*/C_{2,D} 跨过 1,再上去就被 vanilla decoding 反杀。

A.4 i.i.d. α\alpha 下的期望接受 token 数

E=j=0kαj=(1αk+1)/(1α)E = \sum_{j=0}^{k} \alpha^j = (1 - \alpha^{k+1})/(1-\alpha)j=0j=0 项是 verifier 自己那一 token,与 drafter 是否被接受无关。decode 长度 gg 下,每请求周期数 g/Eg/E

A.5 为什么 C2C_2 里把 verifier 项视为 kk 无关

如果 verification 在负载相关项上严格 O(k)O(k)c2,vc_{2,v} 就该随 kk 走。经验上 verifier 的负载相关项几乎与 kk 无关——主导成本是权重与 KV cache 的流式访问,与 token 数关系不大。论文把残留的微小 kk 依赖塞进 kc2,dk \cdot c_{2,d},效果更好。


9. 一个手算示例:怎么把模型套到具体部署上

公式只有在你算过几个数之后才会真正被信任。下面构造一个与论文 Qwen3-8B / 1024 prefill / 1024 decode / 单 A100 大致一致的虚构场景,数值只用于说明问题,定性形状参考图 2–4。

9.1 设置

假设线上 profile 给出 dense 基线:

  • C1,D=1.20sC_{1,D} = 1.20\,\text{s}C2,D=0.07s/reqC_{2,D} = 0.07\,\text{s/req}

校验:RPS=10\text{RPS} = 10LD=1.20/(110×0.07)=1.20/0.30=4.0sL_D = 1.20 / (1 - 10 \times 0.07) = 1.20 / 0.30 = 4.0\,\text{s}RPS=12\text{RPS} = 12LD=1.20/(10.84)=7.5sL_D = 1.20 / (1 - 0.84) = 7.5\,\text{s}RPS=13\text{RPS} = 13LD=1.20/(10.91)=13.3sL_D = 1.20 / (1 - 0.91) = 13.3\,\text{s}。延迟墙在 RPS1/C2,D=14.3\text{RPS} \to 1/C_{2,D} = 14.3 附近立陡,这就是 Little 定律的几何含义。

9.2 SD 配置 A:激进 draft

EAGLE-3 drafter、k=6k = 6、实测 α=0.75\alpha = 0.75,则 E=(10.757)/(10.75)=3.466E = (1 - 0.75^7)/(1 - 0.75) = 3.466 token / 周期。verifier 跑 g/E=1024/3.466295g / E = 1024 / 3.466 \approx 295 周期/请求,远少于 dense 的 1024。设 speculation-aware 拟合给出:

  • C1,SD,A=0.78sC_{1,\text{SD,A}} = 0.78\,\text{s}C2,SD,A=0.085s/reqC_{2,\text{SD,A}} = 0.085\,\text{s/req}

C1,R=0.65C_{1,R} = 0.65C2,R=1.21C_{2,R} = 1.21。套 (2):

  • Speedup(0)=1/0.65=1.54\text{Speedup}(0) = 1/0.65 = 1.54。同步下 SD 比 dense 快 54%。
  • Speedup(RPS=8)=(1/0.65)(1+(11.21)×8×0.0718×0.07)=1.54×(10.21×1.273)=1.13\text{Speedup}(\text{RPS} = 8) = (1/0.65)(1 + (1 - 1.21) \times \frac{8 \times 0.07}{1 - 8 \times 0.07}) = 1.54 \times (1 - 0.21 \times 1.273) = 1.13。RPS 升到 8,加速从 1.54 跌到 1.13。
  • 反超临界:r=(0.35)/(0.56)=0.625r^* = (-0.35)/(-0.56) = 0.625,即 RPS=0.625/0.07=8.9\text{RPS}^* = 0.625 / 0.07 = 8.9

也就是说一旦 RPS>8.9\text{RPS} > 8.9,配置 A 比纯 decode 更慢。如果你的部署 RPS 经常落在 9–12 之间,A 在峰值时主动有害

9.3 SD 配置 B:保守 draft

kk 降到 2,α\alpha 仍 0.75:E=(10.753)/0.25=2.31E = (1 - 0.75^3)/0.25 = 2.31。verifier 跑 1024/2.314431024/2.31 \approx 443 周期。kk 变小让 g/Eg/E 升高、但每周期 drafter 贡献也变小。设:

  • C1,SD,B=0.92C_{1,\text{SD,B}} = 0.92 s,C2,SD,B=0.072C_{2,\text{SD,B}} = 0.072 s/req。

C1,R=0.77C_{1,R} = 0.77C2,R=1.03C_{2,R} = 1.03

  • Speedup(0)=1.30\text{Speedup}(0) = 1.30,比 A 低。
  • Speedup(RPS=8)=(1/0.77)(1+(11.03)×1.273)=1.30×0.962=1.25\text{Speedup}(\text{RPS} = 8) = (1/0.77)(1 + (1 - 1.03) \times 1.273) = 1.30 \times 0.962 = 1.25
  • 反超临界:r=0.23/0.26=0.88r^* = -0.23 / -0.26 = 0.88RPS=12.6\text{RPS}^* = 12.6

B 的同步天花板更低、但反超阈值高得多。A 与 B 在某个 RPS 处加速相等——线性近似下大约在 rcross(1.541.30)/((1.54×0.21)(1.30×0.03))0.84r_{\text{cross}} \approx (1.54 - 1.30) / ((1.54 \times 0.21) - (1.30 \times 0.03)) \approx 0.84,即 RPScross12\text{RPS}_{\text{cross}} \approx 12。已经超过 A 的反超阈值,所以更朴素的结论是:RPS < 8.9 用 A,RPS > 8.9 切到 B(或退化到 dense)。更精细的搜索会在中等 RPS 区段也偏向 B。

9.4 部署启示

同一个 drafter、同一个 verifier、同一个 α\alpha,仅仅 kk 不同就给出两条完全不同的加速曲线。一个能根据 (α,RPS)(\alpha, \text{RPS}) 动态选 kk 的控制器,可以在整个工作区间让加速保持 >1> 1;而固定 kk 的部署要么低负载吃亏(B)、要么高负载主动崩盘(A)。这就是论文最可被直接落地的工程含义。


10. 与近邻工作的对照

把这篇放到三条邻近线索上看会更清楚。

TurboSpec / SD goodput 优化(Liu 等,2024):goodput 路线把 SD 配置当成经验搜索——给一段工作负载 trace,找出在 SLO 下让 tokens/sec 最大的 kk 与 drafter。这种方法工程上有用,但不解释为什么 A 比 B 好。Kong 等人的模型是其互补品:它可以作为 goodput 搜索的内层,省下大量真实测量。

Sarathi-Serve / chunked prefill 调度(Agrawal 等,2024):Sarathi-Serve 是调度类工作——它改变 prefill 和 decode 如何交错。Kong 等人把调度作为黑盒、用拟合得到的 C2C_2 概括它。两类工作正交但耦合:更好的调度器会改 C2C_2,从而改变 SD 的反超阈值。一个自然延伸是:在原版 vLLM 与 Sarathi-Serve 上各拟合一次模型,报告 C2C_2 的差值。

DistServe / Splitwise(解耦服务):把 prefill / decode 解耦到不同 GPU 池后,成本结构变化很大。(1) 式仍然适用,但要按池子分别拟合。SD 活在 decode 池里,因此加速分析直接迁移、只是数值换了。这是论文留下的最干净的一条扩展空间。

Roofline / 算术强度:Kong 等人的分解可以看作把 roofline 从 kernel 级抬升到 request 级。Roofline 写「固定成本 + 每字节成本」,他们写「固定成本 + 每并发请求成本」。审美一致:在成本侧保持线性,再用 Little 定律把并发翻译成负载。


11. 我会怎么写下一篇

如果让我跟进,按价值排序的三条方向:

  1. 带稳定性证明的闭环 kk 控制器。 用滑窗估计 (α,RPS)(\alpha, \text{RPS}),从 speculation-aware 拟合得到的查找表里选 kk^*,并对(或经验证明)控制器不会震荡。切换成本要算进去——改 kk 会改 kernel 选型与 KV-cache 预算。这是运维场景下最实用的延伸。
  2. 尾延迟扩展。 在 M/G/1-with-batch 假设下做排队分析,从 (C1,C2,α,k)(C_1, C_2, \alpha, k) 预测 p95/p99。哪怕给一个粗糙的上界,也比附录里那句「p99 大致也行」要有用。
  3. MoE expert-parallel 校正。 现有 φ(T)\varphi(T) 假设 expert 路由 i.i.d. 均匀。expert parallel 部署会增加跨节点通信,其成本依赖于哪个请求打到哪些热 expert。一个考虑了非均匀 expert 热点的 φ\varphi 会把 MoE 校正推到多节点服务上。

11.2 我会怎么把模型上线:监控清单

如果让我把这套模型放进自己的服务栈,我会按下面这个清单走,作为一种「最小可上线」配方。

第一步:拟合 dense 基线。 用 GuideLLM 或自己的 load generator,在目标 (prefill, decode, 模型大小) 配置下扫 9 个 RPS,从同步到接近吞吐天花板。SciPy 拟合得到 (C1,D,C2,D)(C_{1,D}, C_{2,D})。这是你之后一切判断的锚点,要先做、要存盘。

第二步:拟合 SD speculation-aware 模型。 关闭 vLLM rejection sampler 的真实行为,把 α\alpha 显式钉在几个值上(0.5,0.7,0.90.5, 0.7, 0.9);kk{1,2,4,6,8}\{1, 2, 4, 6, 8\}。每个 (α,k)(\alpha, k) 组合扫 9 个 RPS 得到一组 LL 测量。然后联合拟合得到 6 个 c,c_{*,*}

第三步:检查 C1,RC_{1,R}C2,RC_{2,R} 对每个 (α,k)(\alpha, k) 计算这两个比值。C1,R<1C_{1,R} < 1 是正常预期,C1,R1C_{1,R} \geq 1 说明 drafter 的额外开销已经吃掉了 verifier 的节省,建议换 drafter。C2,R>1C_{2,R} > 1 是「同步加速」红旗,记下对应 RPS\text{RPS}^*

第四步:测线上真实 α\alpha 临时打开 rejection sampler 的统计,跟踪一段时间真实输入上的接受率分布。注意 α\alpha 会随 prompt 风格、用户语种、上下文长度而异——做完整分布而不是单一均值。

第五步:建立 (α,RPS)k(α, \text{RPS}) \to k^* 查找表。 对几个有代表性的 α\alpha 值,画出对每个 kk 的 Speedup-vs-RPS 曲线,在每个 RPS 处取 Speedup 最大的 kk。这个表小到可以直接打成常量数组。

第六步:在线选 kk 用滑窗(比如 30 秒)估当前 RPS,结合最近 α\alpha 估计,从查找表里读 kk^*。切换 kk 时注意:vLLM 内部 KV-cache 预算与 kernel 选型会变,建议加最小切换间隔(比如 60 秒)防止震荡。

第七步:监控四条曲线。 在 Grafana 面板里至少加四条线:

  • 实测 LL(平均);
  • 模型预测 L^=C1,EFF/(1RPSC2,EFF)\hat L = C_{1,\text{EFF}} / (1 - \text{RPS} \cdot C_{2,\text{EFF}})
  • 当前 C2,RC_{2,R}
  • SD vs dense 的 speedup(即 Ldense baseline/LL_{\text{dense baseline}} / L)。

第二条与第一条偏离过远说明拟合系数过时(典型原因:vLLM 版本更新、调度器策略改了、KV cache 配置变了)——这时该重新拟合。

第八步:定期重拟合。 每两周或每次重大栈变更后跑一次步骤 1–3,把系数滚动更新。论文也强调系数是「系统相关」的,不要长期吃老本。


11.3 这套方法给不同角色的读者的不同价值

给模型研究员: 这篇是「请评估你的 SD 方法时报告 C1,RC_{1,R}C2,RC_{2,R}」的标准模板。仅报告 batch=1 的加速比已经不够,会议读者完全有理由要求看负载相关分量。

给推理服务工程师: 这篇给你一个工具,把「为什么我的 SD 在生产里加速没了」从玄学变成数学。可以直接拿 (2) 解释给上司。

给云厂商架构师: 1/C2,D1/C_{2,D} 是单实例的硬上限。它告诉你什么时候应该横向扩容、什么时候只是 SD 配置选错了。如果是后者,扩容是浪费钱。

给 ML compiler 团队: C2C_2 拆解到 (c2,p,c2,v,c2,d)(c_{2,p}, c_{2,v}, c_{2,d}) 之后,你可以反过来定位哪一段最值得编译器优化。例如 c2,dc_{2,d} 显著大于 drafter 参数量的线性预测说明 drafter 的 launch overhead 是瓶颈,那就该 fuse kernel。


11.5 我在读这篇论文时被纠正的几个误解

读完后我对几个流行直觉做了修正,列在这里给可能读得快的工程师同伴。

误解 1:「SD 加速比可以从 batch=1 实验外推到生产。」 这是这篇论文最直接打脸的对象。零负载加速 1/C1,R1/C_{1,R} 只是双曲曲线在 r=0r=0 的截距,真正决定生产体验的是 C2,RC_{2,R} 的符号与大小。

误解 2:「draft 长度 kk 越大越好。」 在 batch=1 下大 kk 摊销 verifier 开销确实诱人——这是 Leviathan 等人原始论文里的核心论点之一。但论文 (b) 图清楚显示,最小化 C2,RC_{2,R}kk 远小于最小化 C1,RC_{1,R}kk。生产里两者得分别选。

误解 3:「EAGLE-3 解决了 SD 的负载问题。」 EAGLE-3 把 drafter 做得更精、α\alpha 更高、c1,dc_{1,d}c2,dc_{2,d} 更小,但它没有改变 C2,RC_{2,R} 必须 <1< 1 才能在负载下不退化的本质。在论文的实验里,即使是 EAGLE-3 drafter,C2,R>1C_{2,R} > 1 的情况仍然普遍。

误解 4:「MoE 模型用 SD 没有意义。」 论文显示低负载下 MoE 受益于稀疏激活——SD 与稀疏激活两层加速可以叠加。但要小心 φ(T)\varphi(T)kk 增大时反弹,导致 verifier 阶段触碰更多 expert、抵消稀疏红利。这又一次落回到「按负载选小 kk」的策略。

误解 5:「saturation 区延迟可以靠堆 GPU 摆平。」 加更多 GPU 通常会同时改 C1,DC_{1,D}C2,DC_{2,D},但前者下降更快、后者下降更慢,效果是把整个曲线左下平移、把饱和阈值往右推一点。它不会让 C2,R>1C_{2,R} > 1 的配置自动变成 C2,R<1C_{2,R} < 1——SD 配置选不对,再堆 GPU 也救不回来。


12. 个人结论

我会把这个加速公式贴在便利贴上。它正是我每次在 vLLM 里调 SD 配置时都缺的那一块——能把同行抱怨了一年的「SD 加速在生产里消失」的现象,钉死成一个可计算的关系式。论文短、范围窄、有用;我宁愿读十篇这样的工作,而不是再来一篇 80 页的系统 benchmark。


笔记结束。