5.4 投机解码的收益边界
建立平均提交长度与完整轮耗时的性能账本,推导加速条件与投机长度的收益递减,分析并发、长上下文、量化、调度与流式延迟对收益的影响,以及动态投机长度的思路
“草稿接受率 80%,是不是一定变快?“这是投机解码最常见的误判。接受率只说明猜得准不准,没说明猜一次花多久、验证一段草稿要多少算力、多出来的缓存挤掉了多少请求。每轮提交的 Token 多了,每轮的耗时也长了,两者相除才是真正的收益。
本节先用一个简化账本推导加速条件,再把真实服务中的并发、长上下文、量化、调度和流式输出逐个加进来。目标不是给出一个通用的”最佳投机长度”,而是让你能解释:为什么这组条件下变快了,换一组条件却不值得。
⏰ 信息时效:公式推导与框架无关;vLLM 相关的建议、参数与限制基于 v0.32.0(2026-10-18 发布)文档。接受率、平均接受长度的定义见第 5.2 节第 7 小节。
📑 目录
- 1. 先定义收益:一轮提交多少 Token
- 2. 性能账本:从接受前缀推导加速条件
- 3. 投机长度:多猜几个何时变成浪费
- 4. 代码与对话:任务标签不是性能结论
- 5. 高并发与长上下文:空余算力会变化
- 6. 与量化叠加:先明确正确性基准
- 7. 调度与流式输出:一次推进变长以后
- 8. 动手算账与部署门槛
- 总结
- 自我检验清单
- 参考资料
1. 先定义收益:一轮提交多少 Token
一轮提出 个候选,记 为连续被接受的前缀长度。没有被 EOS 或长度上限截断时,这一轮提交的 Token 数是:
多出来的 1 是首个拒绝位置的修正 Token,或全部接受后的额外 Token。所以哪怕一个候选都没通过,这一轮也能前进一步。
📌 关键点:“至少前进一个 Token”是进度保证,不是性能保证。全军覆没的一轮,花费的是 Draft 加验证的时间,却只换来普通 Decode 一步的产出。
2. 性能账本:从接受前缀推导加速条件
2.1 预期提交长度
对取非负整数的 ,有尾概率恒等式:
记 为”前 个都已接受时,第 个也被接受”的条件概率。由链式法则:
这一步不需要假设各位置相互独立。如果再简化成所有 都等于同一个 :
时取极限 。动态长度和树形方案每轮候选数不同,应直接用实测的平均接受长度 。
2.2 分母是完整的一轮
记普通 Decode 每个 Token 的平均耗时为 ,投机一轮的平均耗时为:
包括采样、缓存维护、CPU/GPU 同步和调度。在单请求、稳定 Decode、忽略首尾边界轮的简化条件下,加速比为:
要变快,必须满足 :一轮的耗时要少于普通 Decode 生成同样多 Token 的时间。实测时用”总提交 Token 数 / 总耗时”计算,不要对每轮的速度比取算术平均。
2.3 验证不是免费的
一次验证 个位置能复用同一次权重读取,提高算术强度。但计算量、Attention、候选 KV 写入和 Logits 处理都随位置数增长。小 Batch 时 接近 ;候选长、Batch 大或上下文长时,它会明显超过 。
还是主编的比喻:审一段稿比自己写快,但审十页不可能和审一句话一样快,助手写稿的时间也得算进去。
3. 投机长度:多猜几个何时变成浪费
在固定 的简化模型里,把候选从 个加到 个,预期提交长度只增加:
越往后,前面全部通过的机会越小。 时,第 1 个候选能带来 0.8 个预期 Token,第 8 个只能带来约 0.168 个。可 Draft 和验证每多一个位置的成本并不会跟着下降,到某个长度之后,多猜就成了净亏损。
3.1 深度与宽度是两种预算
链式投机只调深度;树形方案(第 5.3 节)还要调每层的分支数。树越宽,找到可接受路径的机会越大,但没被选中的节点也全要计算。所以只报告”投机深度 4”不够,还要报告每轮实际验证了多少个节点。
3.2 动态长度:随负载调整
最优长度会随负载变化。vLLM 的 Dynamic Speculative Decoding 用 num_speculative_tokens_per_batch_size 按并发分段设置长度,例如 [[1, 64, 3], [65, 128, 1], [129, 512, 0]] 表示并发 1–64 时猜 3 个、65–128 时猜 1 个、129 以上不投机。文档给出的理由是:Batch 为 、每个请求猜 个候选时,验证的有效 Batch 约为 ,超过某个临界值后反而拖慢 TPOT。
v0.32.0 中该功能在 EAGLE、EAGLE-3 和 DFlash 上测过,开启 Data Parallel 时会自动退回固定长度。另有 Adaptive Verification 为每个请求决定验证长度,支持 EAGLE、MTP、DFlash、DSpark 和独立 Draft 这类基于模型的草稿方法:DSpark 用自带的置信度头,其余方法用在线估计的接受概率。它要求完整 CUDA Graph,不支持 LoRA 和 Pipeline Parallel。
好的策略优化的是”单位时间推进多少合格输出”,而不是单独把接受率推到最高。
4. 代码与对话:任务标签不是性能结论
“代码接受率高、开放对话收益有限”是常见的经验,但真正起作用的是历史重复程度和分布匹配:
| 📊 场景 | 可能有利的原因 | 仍会失败的情况 |
|---|---|---|
| 代码编辑 | 大段原文保留,标识符反复出现 | 改动集中,续写方向变化大 |
| 结构化抽取 | 输出复制输入,格式固定 | Prompt 里没有答案,或格式约束与候选冲突 |
| Agent 轨迹 | 重复步骤、相似续写多 | 工具返回结果改变了后续走向 |
| 开放对话 | 强 Draft 仍可能与 Target 合拍 | 表达多样,便宜的草稿频繁被否 |
温度、Top-p、模型的对齐方式和 Prompt 分布都会影响接受情况。一个”原样复制代码”的题目代表不了所有代码任务,一个高温创作题也代表不了所有对话。
⚠️ 注意:随机 Token 的 Benchmark 适合控制性能变量,但它没有真实业务的重复结构。要评估 N-gram/Suffix 的实际收益,必须用真实 Prompt。
5. 高并发与长上下文:空余算力会变化
5.1 Continuous Batching 已经吃掉了一部分空闲
低并发时,每步只处理少数请求,Target 的计算单元大量空闲,投机正好利用这部分算力。并发上来后,Continuous Batching 让一次权重读取服务更多请求,普通 Decode 自己就把算力用起来了。
这时再加候选,就从”用空闲算力”变成了”和其他请求抢算力”。vLLM 文档也把投机解码定位为降低中低 QPS、访存受限场景下的 Token 间延迟。它的方法选择表给出的定性判断是:EAGLE、MTP 在低 QPS 下收益高、高 QPS 下中到高;N-gram 和 Suffix 在低 QPS 下收益低到中、高 QPS 下中等。文档开头还提到,这类简单方法加速有限,但不会在流量高峰时增加负载。
高并发也不一定无效:更准的 Draft、并行草稿和合适的 Kernel 都可能保住收益。应该按负载画曲线,不要凭一条经验全盘否定。
5.2 长上下文增加读写与临时状态
上下文越长,每个验证位置的 Attention 越贵,候选也要占额外的 KV;独立 Draft 自己的缓存同样随上下文增长。设 Batch 中有 个请求、每个预留约 个候选位置,Target 额外 KV 的粗略量级是:
这个估算忽略了分页取整、lookahead 槽位、共享前缀和树布局,只用来说明多出了哪一项,实际容量以引擎日志为准。
5.3 抢占会吃掉纸面收益
Draft 权重和临时缓存减少了可用 KV 空间,可能触发更多抢占和重算,或者让引擎接纳更少的请求。单轮验证更快、整个请求完成得反而更慢,这两件事并不矛盾。
因此记录接受长度时,要同时记录 KV 容量、抢占次数、成功请求数和实际吞吐。
6. 与量化叠加:先明确正确性基准
6.1 量化 Draft 与量化 Target 是两回事
- 只量化 Draft:改变的是提议分布 。按第 5.1 节的证明,最终输出仍是未量化 Target 的 ,变化的只是速度和接受率。
- 量化 Target:验证基准变成了 。投机解码保证与量化后的模型一致,不会把它恢复成原始精度。
- 近似验收 + 量化:两种变化叠加,需要分别评估质量。
EAGLE、MTP 这类读取 Target 特征的模块,训练时见的是原精度特征。Target 换了量化方案,特征分布随之变化,接受率可能下降,需要重新测量。v0.32.0 的发布说明里有一项修复,针对的是 DeepSeek-V4 系列 NVFP4 检查点上 Draft 接受率为 0% 的问题。这类组合值得单独验证。
6.2 加速比不能直接相乘
量化和投机缓解的都是 Decode 的访存瓶颈。量化减少了 Target 的权重读取量,本来被访存掩盖的验证计算可能变得更显眼;同时它也释放了显存,为更多候选和请求腾出空间。
所以”量化快 1.5 倍、投机快 2 倍”推不出组合快 3 倍。应分别测原始基线、仅量化、仅投机和组合方案,并写明各自的质量基准。
📌 关键点:第 4 章的精度账本和第 5 章的采样保证,要在同一个 Target 上对齐,才谈得上组合收益。
7. 调度与流式输出:一次推进变长以后
7.1 调度器要管的不止一个新位置
开启投机后,引擎每步要为候选预留位置、分配 Token 预算,再根据接受结果更新每个请求的真实进度。同一个 Batch 里,不同请求的候选数、接受长度和剩余输出额度都可能不同。
下图上半部分是调度器的一轮,下半部分是同一 Batch 里三个请求在这一轮结束时的状态(绿格是接受的候选,蓝格是 Target 补的 Token,灰色虚线格是回退的 KV 槽位):
这条链路会和 Continuous Batching、Chunked Prefill、CUDA Graph 的固定形状以及异步调度相互影响。例如 Dynamic SD 在 v0.32.0 中只有 V2 Model Runner 支持完整 CUDA Graph。哪些组合可用,以所用版本的文档和启动日志为准。
7.2 TPOT 变小,出字不一定更均匀
投机解码一次验证可能返回好几个 Token,下一轮又要等一阵,用户看到的流式输出更像”一段一段地冒出来”。
- TPOT:首 Token 之后,平均每个 Token 的时间。
- ITL / 流式间隔:相邻两次返回之间的停顿。一个 chunk 里可能有多个 Token,不能直接等同于单 Token 耗时。
- TTFT:包含排队和 Prefill,减少 Decode 轮数对它帮助不大。
评估时要把 TTFT、TPOT、流式停顿和端到端时间放在一起看。实时交互场景还要看 P95/P99,以及业务能接受的最长停顿。
7.3 停止条件与结构化输出要及时生效
接受的前缀里如果出现 EOS、Stop String 或达到输出上限,只提交结束之前的部分。结构化输出、Tool Calling 这类带状态的约束也要随每个候选前缀更新;vLLM 逐请求指标里的 num_draft_tokens,已经扣除了被结构化输出约束提前作废的候选。
遇到问题时,先让普通生成和最简单的投机配置跑通,再逐个加功能,方便定位是哪一步改变了正确性或性能。
8. 动手算账与部署门槛
8.1 接受情况相同,分母可以完全不同
下面是纯标准库的假设成本模型,时间常数只为演示,不对应任何 GPU 或真实模型。
def expected_length(conditional_acceptance):
"""E[L] = 1 + Σ Pr(K >= i),输入为各位置的条件接受概率 a_i。"""
survival, total = 1.0, 1.0
for a in conditional_acceptance:
assert 0 <= a <= 1
survival *= a
total += survival
return total
base_ms = 10.0 # 假设普通 Decode 每 Token 10 ms
print(f"{'γ':>2} {'E[L]':>6} {'便宜草稿':>8} {'昂贵草稿':>8}")
for gamma in [1, 2, 4, 8]:
length = expected_length([0.8] * gamma)
cheap_round = 10 + 0.7 * gamma + 0.1 * gamma ** 2 # 草稿快、验证增长慢
costly_round = 10 + 5.0 * gamma + 0.8 * gamma ** 2 # 草稿慢、验证增长快
print(f"{gamma:>2} {length:>6.3f} "
f"{base_ms * length / cheap_round:>8.3f} "
f"{base_ms * length / costly_round:>8.3f}")
assert expected_length([0.0] * 4) == 1
assert expected_length([1.0] * 4) == 5
两组方案的条件接受概率完全相同:便宜的那组在 时达到约 2.3 倍,到 反而回落;昂贵的那组只在 时略快, 起就比普通 Decode 还慢。实际实验时,把两个假设的成本函数换成实测的轮耗时和提交统计即可。
8.2 上线前的四道门槛
| 🎯 门槛 | 要求 | 不通过时 |
|---|---|---|
| 正确性 | Target、采样参数与缓存历史对齐 | 排查实现与配置 |
| 质量 | 业务指标达到既定基准 | 拆开量化、训练、近似验收各自的影响 |
| 延迟 | 目标负载下 TTFT/TPOT 与流式停顿达标 | 调候选长度,或换一种方法 |
| 容量与成本 | 吞吐、KV 容量与资源成本合理 | 比较更小的 Draft、无模型方案,或关闭投机 |
平均接受长度变大、轮耗时也变长、TPOT 却没改善时,沿 Draft、验证、采样和缓存维护逐项定位。若只有空载时效果好,就在目标负载下重测,再决定是否按场景启用,或交给 Dynamic SD 自动降级。
📝 总结
- 每轮提交长度是连续接受前缀加 1,边界轮会被停止条件截断。
- 不要求位置独立;固定条件接受概率 只是简化模型。
- 加速的条件是完整一轮的耗时低于普通 Decode 生成同样多 Token 的时间。
- 第 个候选只多带来 个预期 Token,收益递减;树的宽度同样有成本。
- Dynamic SD 按并发分段调整投机长度,高并发时可以自动减少甚至关闭投机。
- 代码和对话只是实验分组,真正解释差异的是重复结构和分布匹配。
- 高并发、长上下文和抢占会改变空闲算力与显存预算,vLLM 把投机定位在中低 QPS 场景。
- 量化 Draft 不改变输出基准,量化 Target 会;两种加速比不能直接相乘。
- TPOT、流式停顿、容量与业务质量共同决定是否部署。
🎯 自我检验清单
- 能从连续接受前缀推导出预期提交长度
- 能说明条件接受概率链式相乘为什么不需要独立假设
- 能写出包含 Draft、验证与其他开销的加速条件
- 能用 解释投机长度的收益递减
- 能为 vLLM 写出一份按并发分段的
num_speculative_tokens_per_batch_size配置,并解释它的依据 - 能说明随机 Prompt 为什么不足以评估 N-gram/Suffix
- 能分析高并发、长上下文与抢占如何吃掉投机收益
- 能区分量化 Draft 与量化 Target 对输出基准的影响
- 能解释 TPOT 改善为什么不保证流式停顿改善
- 能按正确性、质量、延迟与容量四道门槛做部署决策
📚 参考资料
- Fast Inference from Transformers via Speculative Decoding(Leviathan et al., 2023)
- Accelerating Large Language Model Decoding with Speculative Sampling(Chen et al., 2023)
- vLLM — Speculative Decoding(方法选择表)
- vLLM — Dynamic Speculative Decoding
- vLLM — Adaptive Verification
- vLLM — Per-Request Acceptance Metrics
- vLLM v0.32.0 Release Notes
- vLLM v0.32.0 — V1 Scheduler