2026-09 · 在一台 32GB 显存的机器上,对两条不同的投机解码路径做参数扫描的记录
投机解码(speculative decoding)的直觉很顺:草稿模型一次多猜几个 token,主模型并行验证,猜中了就等于白赚。 那么"猜得越多越快"听起来理所当然。我一开始也是这么配的,直到发现默认值附近的性能差异比想象中小得多, 而有些参数一旦调错,是纯粹在亏速度。
测试环境是单张 32GB 卡、27B 量级的模型、200K 上下文、KV 用 4 bit 量化、四个并发槽共享一个统一 KV 池。 每换一次参数都要重启进程(这些是启动期参数),所以只能串行测,一个格子大约四十多秒。
第一条路径(外挂的扩散式草稿模型)在 4 个并发槽下的实测:
| 草稿长度上限 | 吞吐 (tok/s) | 接受率 | 平均接受长度 |
|---|---|---|---|
| 2 | 95.05 | 53.7% | 2.06 |
| 3 | 99.63 | 44.9% | 2.33 |
| 4 | 97.42 | 33.9% | 2.33 |
| 5 及以上 | 起不来(草稿的并行预填充计算缓冲还要约 1.08 GiB,显存不够) | ||
接受率从 53.7% 一路掉到 33.9%,而"平均接受长度"在 2.33 就封顶了——也就是说草稿猜得再长, 主模型也不认,多猜的部分只是白白占用一次前向。峰值出现在 3,不是 1(浪费验证算力)也不是 5(放不下)。
这里有个坑:n≥5 起不来,很容易让人以为"峰值是被显存墙砍出来的,如果能跑起来肯定更快"。 所以我降了一个并发槽换出约 3.2 GiB 余量,让 n=5 和 n=6 真的跑起来——结果是 94.4 和 88.1,更慢。 峰值是真的,不是被显存限制假性截断的。
两条路径都有一个草稿侧的置信度门(低于阈值就提前截断草稿链)。在第一条路径上扫下来:
| 阈值 | 0 | 0.5 | 0.75 | 0.9 |
|---|---|---|---|---|
| 吞吐 (tok/s) | 99.6 | 87.3 | 79.5 | 72.2 |
| 接受率 | 44.9% | 67.5% | 88.3% | 96.7% |
接受率涨到 96.7%,吞吐掉到 72%。因为它是靠砍短草稿链换来高接受率的——报表上那个数字变好看了,实际产出变少了。 这是我在整个测试里最不满意的一条:如果只看接受率调参,方向会完全反。
有意思的是另一条路径(模型自带的多 token 预测头)上,同样的参数在 0.2 附近是"免费"的:0.0 与 0.2 的实测吞吐差在 0.8% 以内。 也就是说这个参数该不该设,取决于草稿是怎么产生的,不能跨路径照搬。
同一份配置、同一批 prompt,第一轮扫出来的中位数是 133.7,第二轮 128.9,而交错复测四轮稳定在 99.6 上下—— 差别来自 prompt 本身:三条测试文本(写代码、讲技术概念、中文描写)的接受率天生不同, 取中位数等于在比"哪条 prompt 排中间"。
我最后用的方法:
顺带一条:投机解码本身会改变输出。固定 seed、温度 0 的情况下,"完全不跑投机"和任何投机配置大约在第 40 个字开始分叉, 而调那个置信度门反而不会改输出(它只决定要不要提供草稿,不否决主模型已经接受的东西)。 动文本的是投机这件事本身,不是门限。
扩散式草稿的置信度门我没有扫(只扫了另一条路径),两条路径的绝对速度差也还没有在同一台机器上做过严格对照。 如果后面补了会直接改在这一篇里。