投机解码的经济学:同一个功能,9B 上 +41%,35B 上打平

2026-09-02

English · 中文

投机解码(我们用的是 MTP:模型自带的 next-n 草稿头)的原理一句话能讲完: 小代价猜 k 个 token,大模型一次 verify,猜中几个收几个。但"到底赚不赚" 取决于一个很少被拆开算的量:verify 的边际成本

我们把它测出来了。同会话四个 k 值扫描,48 轮累计:

verify(m) ≈ 9 ms + 5.5 ms × m        (m = k+1 行)

固定项 9ms ≈ 一个普通 decode 步。为什么多 verify 一行只要 5.5ms、而不是 再付一遍全部权重?因为 m 行 verify 走的是 batch GEMV:权重从显存读 一次,m 行共享。稠密部分的带宽账在 m∈[2,8] 时几乎是免费的。

架构决定赚不赚

那 5.5ms 的边际里藏着关键:对 MoE 模型,每行激活的专家不同,专家权重 不可共享 —— 每多一行就多读约 0.46GB 专家,约 3.2ms。这一项稠密模型没有。

后果直接写在结果里。同一套 MTP 实现:

模型架构结论
Qwen3.5-35B-A3BMoEaccept ≈ 0.8 才打平,上限也只有 ~1.19×
Qwen3.5-9B稠密edit +41% · code +24% · chat +15% · en +6%,无负项

同一个功能,一边是"约等于不赚",一边是产品级收益 —— 差别就是每行边际里 有没有那 3.2ms 的专家项。投机解码的性价比不是实现细节,是架构属性。

深度 k 的甜点:扫出来,不是猜出来

9B 上把 k 从 1 扫到 6(单并发,edit/en/code 三负载,基线 45.1/45.7/45.8 tok/s):

k=1   53.3 / 49.3 / 53.5
k=2   56.3 / 47.2 / 53.0
k=3   63.3 / 48.5 / 56.6    ← 合计最优,服务定值
k=4   65.1 / 46.5 / 48.4    (只利 edit,code 塌)
k=5+  全面衰退

acceptance 随深度单调下降(英文散文 k=1 时 0.85,k=3 时 0.59):猜得越深, 后面的 token 越接近瞎猜。不同负载的最优 k 不同 —— 散文偏浅、代码和编辑 撑得住深 —— 但 k=3 是全局最优的固定值,这就是服务的默认。

门控:让它只在赚的地方开

最有意思的设计不是内核,是一个阈值:滚动 acceptance 低于 0.60 就自动 退回普通解码(AS_SPEC_MIN_ACCEPT=0.60)。

这个门的效果超出它的本意。agent 负载 —— 改文件、发工具调用、生成 JSON —— acceptance 天然高(结构化文本可预测,35B 上 mid-edit 实测 0.92);中文散文 天然低(0.28-0.5)。于是一个简单的阈值自动实现了"编辑场景开、散文场景 关"的正确形态,不需要负载分类器,不需要用户配置。

35B 上它兜住下限:散文触发门、退回纯解码,mid-edit 负载 91 → 97.6 tok/s (+7%)照拿。9B 上它几乎不触发,全场景放行。

一笔要诚实写出来的账:快,但不省电

自制的 IOReport 采样器(免 root)量了解码窗的功耗,9B 英文思考负载:

吞吐功率每 token 能量
k=045.8 tok/s51.7 W1.14 J
k=352.3 tok/s64.3 W1.26 J (+10%)

verify 的批量计算抬高了瞬时占用 —— MTP 用能量换时间,不是免费午餐。 所以 App 形态里它对应两个档位:性能档 k=3,省电档 k=0。插电的 Mac mini 和电池上的 MacBook,答案本来就该不同。

收尾

一个功能从"听起来很快"到"知道什么时候开",中间隔着:一个成本模型 (9 + 5.5m)、一条架构分界线(专家可不可共享)、一次 k 扫描(甜点在 3)、 一个自动门(0.60),和一笔功耗账(+10% J/tok)。这些都测完之后, "默认值该是什么"就不再是观点,是查表。