09 · MTP 调参:num_speculative_tokens 的选择及其负载相关性
本文只讨论一个主题:MTP(Multi-Token Prediction)投机解码中,真正可调的参数是哪一个、该取什么值,以及为什么"调好了"这个结论本身依赖负载形态。MTP 头的补齐过程见本系列关于 MTP 落地的文章,此处不再展开。
文章目录
- [09 · MTP 调参:num_speculative_tokens 的选择及其负载相关性](#09 · MTP 调参:num_speculative_tokens 的选择及其负载相关性)
-
- [0. 结论速览](#0. 结论速览)
- [1. 速度模型:MTP 摊薄的到底是什么](#1. 速度模型:MTP 摊薄的到底是什么)
- [2. `num_speculative_tokens` 扫描:1 / 2 / 4 对照 3](#2.
num_speculative_tokens扫描:1 / 2 / 4 对照 3) -
- [2.1 扫描成本](#2.1 扫描成本)
- [2.2 实测结果(`TP=8/PP=1`,单流 5 次中位)](#2.2 实测结果(
TP=8/PP=1,单流 5 次中位)) - [2.3 判读一:k=3 保留,低并发优势明显](#2.3 判读一:k=3 保留,低并发优势明显)
- [2.4 判读二:`TP=4/PP=2` 形态下 k=3 仍是单流最优](#2.4 判读二:
TP=4/PP=2形态下 k=3 仍是单流最优) - [2.5 为什么 k≥4 必然变慢(用实测参数外推)](#2.5 为什么 k≥4 必然变慢(用实测参数外推))
- [3. 接受长度是负载相关的:跨负载比较无效](#3. 接受长度是负载相关的:跨负载比较无效)
-
- [3.1 同一配置、两种负载,差 20%+](#3.1 同一配置、两种负载,差 20%+)
- [3.2 单流速度的全部波动可由接受率解释](#3.2 单流速度的全部波动可由接受率解释)
- [3.3 派生结论:"单流 100+ tok/s"从何而来](#3.3 派生结论:"单流 100+ tok/s"从何而来)
- [4. 内容受控 A/B:为什么单流点值不可比](#4. 内容受控 A/B:为什么单流点值不可比)
-
- [4.1 一次真实误判](#4.1 一次真实误判)
- [4.2 换成内容受控,差异就消失了](#4.2 换成内容受控,差异就消失了)
- [4.3 工具:`scripts/decode_ab.py`](#4.3 工具:
scripts/decode_ab.py) - [4.4 报数纪律](#4.4 报数纪律)
- [5. 暖机不足:"接受率提升"的假象](#5. 暖机不足:"接受率提升"的假象)
- [6. 跨会话漂移:判定阈值的依据](#6. 跨会话漂移:判定阈值的依据)
- [7. 踩坑与证否](#7. 踩坑与证否)
- [8. 复现命令](#8. 复现命令)
0. 结论速览
- 真正的调参旋钮只有一个:
num_speculative_tokens(记作 k) 。核心公式:tok/s = (1 + 接受率 × k) ÷ 步进耗时。步进耗时几乎恒定(38.1~39.1 ms,±1.3%),唯一变量是接受率。 - k=3 保留为缺省 :
TP=8/PP=1形态下,单流中位 k=1 为 59.0(−16.4%) 、k=2 为 67.1(−5.0%)、k=4 为 67.4(−4.5%),k=3 的 70.6 最优;且 k=4 在接受长度最高(2.615)的情况下聚合吞吐反而 −5.2%。 - 接受长度不是目标函数 :多出的草稿 token 要摊入
1/(k+1)的验证成本。k=3 时每步产出 2.11~3.79 token,取决于内容的可预测性。 - 最优 k 随负载形态变化 :
conc=64时 k=1 +15.0% 、k=2 +15.7% (conc=8时 +15.8% / +16.1%)。⇒NUM_SPEC_TOKENS=2是"批量优先"部署的正式决策点,单流代价仅 −5.0%。 - 接受长度与单流点值均不可跨负载 / 跨单次比较 :同一配置下缺省思考档接受长度 3.09/3.20,
reasoning_effort=low仅 2.65(差 20%+);同一支内不同 prompt 单流速度从 58.8 到 87.6 tok/s,而两支跨内容中位为 71.2 vs 71.3 ⇒ 配置对比只看step_ms+ 跨内容中位。 - 新形态(
TP=4/PP=2)上 k=3 仍是单流最优 :内容受控中位 k=2 65.5 vs k=3 71.9(+9.8%) ,且 k=2 的步进反而快 6.2%(31.65 vs 33.75 ms);外推 k≥4 净 −6%。
1. 速度模型:MTP 摊薄的到底是什么
- 本文只关心 MTP 的速度模型,后续所有调参判断都从下面的式子推出:
bash
接受长度 = 1 + Σ p_i (p_i 为第 i 个草稿位的接受率)
每步产出 token = 1 + 接受率 × k (k=3 时上限 4)
解码 tok/s = 每步产出 ÷ 步进耗时
单流是 latency-bound 的:batch=1 时每步约 28 ms,其中约 80% 是与 token 数无关的固定开销(78 层 all-reduce、MoE 小 GEMM、DSA indexer)。MTP 的价值在于"一步验证多个候选"------k 越大摊薄越狠,但每步也更贵。
- 用引擎自带的计数器验证了这条恒等式。在一段纯测试流量的窗口内累计:
bash
vllm:spec_decode_num_drafts_total = 15,018 <- 步数
vllm:spec_decode_num_draft_tokens_total = 45,054 <- = 15,018 × 3.0,确认 k=3
vllm:spec_decode_num_accepted_tokens_total = 28,603 <- 接受率 63.5%
vllm:generation_tokens_total = 43,643
校验:15,018 × (1 + 28,603/15,018) = 43,621 ≈ 43,643 ✅
这条恒等式是理解后文所有现象的钥匙:服务端权威确认 k=3,步进耗时 ≈38 ms 为常数,唯一变量是接受率。
⚠️ 口径提醒:63.5% 是整容器累计接受率,混合了多种 prompt;单请求窗口内可高到 92.3%、低到 37.0%。两个数都对,但必须说清是哪个。
2. num_speculative_tokens 扫描:1 / 2 / 4 对照 3
2.1 扫描成本
NUM_SPEC_TOKENS=k ./deploy.sh start 表面是改一个环境变量,代价有两层:
- CUDA 图捕获列表随 k 变化 (k=1 →
2,4,...,128;k=2 →3,6,...,192;k=3 →4,8,...,256)。列表由resolve_defaults()按k+1自动推导,不要手写。 - 改
num_spec会改变--compilation-config,编译缓存失效 ,每档重启后需重付 Dynamo 转换 + inductor 编译 +shm_broadcast停顿,实测约 12~15 min/档。
因此每轮实验流程固定为:重启 → 等就绪 → 两轮暖机 → 单流 5 次取中位 → 并发扫 6 档 → 抓接受率与 KV 余量。
2.2 实测结果(TP=8/PP=1,单流 5 次中位)
| k | 单流中位 | conc=1 | conc=4 | conc=8 | conc=16 | conc=32 | conc=64 | 聚合总和 | 接受长度 | KV GiB |
|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 59.0 | 57.9 | 186.2 | 279.8 | 349.1 | 574.3 | 781.2 | 2228 (+6.5%) | 1.80 | 17.83 |
| 2 | 67.1 | 66.9 | 187.2 | 280.6 | 385.8 | 570.8 | 785.5 | 2277 (+8.8%) | 2.28 | 17.79 |
| 3(对照) | 70.6 | 69.3 | 189.2 | 241.7 | 381.8 | 531.2 | 679.1 | 2092 | 2.50 | 17.62 |
| 4 | 67.4 | 68.3 | 166.0 | 218.4 | 370.4 | 500.0 | 661.4 | 1984 (−5.2%) | 2.615 | 16.27 |
粗体 = 该列最优。附注:k=4 的 KV 余量反而少 1.4 GiB (16.27 vs 17.62),因为其捕获列表要覆盖 batch × 5 = 256 个 token,CUDA 图显存占用更大,加深 k 不只是算力代价。
2.3 判读一:k=3 保留,低并发优势明显
- 单流 / 低并发:k=3 最优,k=1 单流 −16.4%(步数摊不开固定开销)、k=2 −5.0%、k=4 −4.5%。"k=2 可能因每步更便宜而反超 k=3"的早期猜测被实测否证。
- 高并发:k≤2 明显更优 。
conc=64:k=1 +15.0%、k=2 +15.7%;conc=32:+8.1% / +7.5%;conc=8:+15.8% / +16.1%。 - k=4 全面更差 (单流 −4.5%、聚合 −5.2%),尽管接受长度最高(2.615)。用
accept/(k+1)粗算(k=1: 0.90、k=2: 0.76、k=3: 0.625)方向正确但幅度高估(预测 k=1 应比 k=3 高 44%,实测仅 15%)⇒ 每步固定开销仍然显著,接受长度不是目标函数。 - ⇒ 缺省取 k=3 (单流 −16% 不可接受);批量优先部署改用
NUM_SPEC_TOKENS=2(单流仅 −5.0%、高并发 +8.8%~+15.7%),作为一个显式部署决策点记录。
2.4 判读二:TP=4/PP=2 形态下 k=3 仍是单流最优
最终为 TP=4/PP=2 后,补测 k=2 与 k=3(同机、同权重,仅改 NUM_SPEC_TOKENS,捕获表自动从 ×4 变为 ×3):
| 指标 | k=2 | k=3(缺省) | 判读 |
|---|---|---|---|
| 单流 · 固定 prompt 自由生成 | 80.0 | 80.0 | ⚠️ 相同是巧合(内容运气) |
| 单流 · 内容受控中位(★可比口径) | 65.5 | 71.9 | k=3 高 9.8% |
| 单流 · 内容受控区间 | 54.2 ~ 82.9 | 58.3 ~ 95.9 | --- |
step_ms(与内容无关) |
31.65 | 33.75 | k=2 步进快 6.2% |
tok/step 中位 |
2.09 | 2.44 | k=3 每步多收 17% |
| conc=2 | 128.6 | 91.5 / 105.1 | k=2 优 |
| conc=4 | 138.6 | 130.9 | 持平 |
| conc=8 | 221.2 | 180.3 / 183.7 | k=2 优 21% |
| conc=16 | 322.7 | 297.1 / 309.9 | k=2 优 6% |
其中两条方法论教训:
- "固定 prompt 自由生成"一行的 80.0 vs 80.0 是巧合:同一配置内跨内容即可散到 58.3~95.9,拿单点值比配置差异无效(见 §4 的内容受控 A/B)。
- 内容受控中位才是可比口径:k=3 高 9.8%,而 k=2 步进快 6.2% ⇒ 差异来自每步多收 17% 的 token,非算术误差。
2.5 为什么 k≥4 必然变慢(用实测参数外推)
无需再扫 k=4。k=3 配置下引擎侧逐位接受率(/metrics 累计 24,868 次 draft):
| 位置 | 接受次数 / draft 数 | 逐位接受率 |
|---|---|---|
| pos0 | 19,638 / 24,868 | 0.790 |
| pos1 | 13,311 / 24,868 | 0.535 |
| pos2 | 7,279 / 24,868 | 0.293 |
⇒ 接受长度 = 1 + p0 + p0p1 + p0p1p2 = 2.34 (日志报 2.21~2.48,吻合)。第 3 个草稿位接受率已只有 29%,第 4 位按趋势约 0.12 ⇒ 加深的边际收益趋零。
代入实测边际成本:step(k) 每加一档约 +9.2% (由 step(0)≈26.5 ms 标定:k=2 实测 31.65 ms、k=3 实测 33.75 ms);k=3→4 多收的 token 仅 p0p1p2p3 ≈ 0.79×0.535×0.293×0.12 ≈ 0.015 tok/step(+0.5% ),步进却 +7.2% ⇒ 净 −6%。
🔴 结论 :k=3 落在单流最优点上,而非保守取值 ;k≥4 会变慢。唯一值得考虑的替代是 k=2 :单流 −9.8%,但
conc=8+21% /conc=16+6%------这是"多用户优先"的取舍,不是"更快"。
单流速度的进一步提升空间,三条路均已量化:
| 路 | 空间 | 状态 |
|---|---|---|
| ① 提高 MTP 接受率 | 由生成内容决定(同配置实测 58.3~95.9 tok/s),配置改不动 | ❌ 不可控 |
② 降低 step_ms |
唯一可控项。实测地板:k=0 ≈26.5 / k=2 31.65 / k=3 33.75 ms | 手段已穷尽* |
| ③ 换 KV dtype | bf16 KV 的 step_ms = 31.6(−6%),代价是上下文减半(512K→256K) |
✅ 可切,待补验收 |
* ② 的手段(EP、NCCL 通信参数、裁剪 CUDA graph、更大的 max_num_batched_tokens 等)均已验证无收益或负收益,详见本系列关于图捕获/编译开关与 NCCL 通信调优的文章。算术上限 = 4 tok/step ÷ 33.75 ms ≈ 118 tok/s ;缺省自由生成 80.0 为上限的 68%,其余 32% 取决于文本可预测性,与配置无关。
3. 接受长度是负载相关的:跨负载比较无效
3.1 同一配置、两种负载,差 20%+
| 工作负载 | 接受长度 | 逐位(累积接受率) |
|---|---|---|
| 探针(缺省思考档,长篇 reasoning) | 3.09 / 3.20 | 0.854 / 0.685 / 0.551 |
基准测试(reasoning_effort=low,简短答案) |
2.65 | 0.789 / 0.531 / 0.335 |
思考文本比简短答案可预测得多。这解开了此前的一个误判:曾有参考项目在类似量化上报告接受长度 3.06,而我们测得 2.4~2.7,并非方草稿质量差,而是口径不一致:在可比的长文本负载下,我们的接受长度同样可达 3.1~3.2。此前的比较是拿"简短答案"对"长文本"。
3.2 单流速度的全部波动可由接受率解释
四种性质截然不同的 prompt,各跑 max_tokens=1024,配置一个字未改:
| prompt 类型 | prompt tok | 接受率 | tok/step | 解码 tok/s |
|---|---|---|---|---|
1 2 3 4 5 ...(几乎确定) |
5,766 | 93.1% | 3.79 | 97.3(实测最高) |
| 普通英文散文(重复句) | 7,001 | 79.8% | 3.39 | 86.6 |
A A A A ...(模型不跟随重复) |
3,001 | 50.9% | 2.53 | 66.6 |
| 伪随机词表(最不可预测) | 5,700 | 37.0% | 2.11 | 54.0(实测最低) |
接受率在 37.0%~93.1% 之间摆动,每步产出 2.11~3.79 token,单流实测区间即为 54.0~97.3 tok/s。由此可推单流算术上下限:
text
接受率 100%(每步 4 token)÷ 步进 38 ms => 105.3 tok/s <- 单流绝对天花板
接受率 0%(每步 1 token)÷ 步进 38 ms => 26.3 tok/s <- 下界(量级参考,非实测)
3.3 派生结论:"单流 100+ tok/s"从何而来
| 口径 | 数值 | 出现场景 | 是否为解码速度 |
|---|---|---|---|
| ① 并发聚合 | conc=2 136 · conc=4 209 · conc=16 318 | 任何并发压测工具 | ❌ 系统吞吐 |
| ② 按 engine step 槽位计数 | 4 ÷ 0.0381 = 105 | 把被拒草稿也算作输出 | ❌ 被拒草稿不是输出 token |
| ③ 输出高度可预测 | 实测 97.3(上限 105) | 复读、续写模板、编号列表、JSON | ✅ 真实解码速度,但不代表日常场景 |
⇒ 单流 100+ 的合理解释只有 ③,且属边缘情况。
4. 内容受控 A/B:为什么单流点值不可比
4.1 一次真实误判
- 对比
TP=4/PP=2的两种层切分(39,39vs41,37)时,使用固定 4-token prompt、自由生成 1024 token:
text
39,39 = 85.3 tok/s vs 41,37 = 72.5 tok/s -> 误判"重切分使单流掉了 15%"
结论是错的:该数字测的是这次生成的文本好不好猜,不是配置差异。
4.2 换成内容受控,差异就消失了
改用 5 类内容受控 prompt(可预测性从高到低)各跑 2 次:
| prompt 类型 | 39,39 tok/s |
41,37 tok/s |
|---|---|---|
| counting(数数) | 76.0 | 67.1 |
| squares(平方表) | 58.8 | 79.7 |
| prose(长散文) | 66.4 | 62.4 |
| repeatA(重复字母) | 76.4 | 73.2 |
| code(长代码) | 87.6 | 91.6 |
| 跨内容中位 | 71.2 | 71.3 |
同一配置内不同 prompt 可散到 58.8~87.6(±20%),而两支中位几乎相同。⇒ 单流 tok/s 点值不可比,可比的只有:
step_ms(内容无关,纯算力/通信)------配置差异的真正载体;- 跨内容中位数(把内容维度平均掉)。
4.3 工具:scripts/decode_ab.py
- 固化为脚本:使用一组确定性、可预测性差异极大的 prompt(counting / squares / prose / repeatA / code),各以
ignore_eos跑满固定 token 数,逐类比较;报告中step_ms(内容无关)与tok/step(内容相关)分开;--compare a.json b.json直接出对比表。
它与另两把尺子的分工:
| 工具 | 口径 | 用途 / 局限 |
|---|---|---|
scripts/bench.py |
端到端朴素口径(分母含排队 + TTFT + HTTP) | 会低估(prompt 越长偏差越大,实测最多 −55.8%);可横向比较,不能当解码速度 |
scripts/decode_speed.py |
纯解码(剔除 prefill) | 准确、可复现(3 次重复极差 0.03%),但内容不受控 |
scripts/decode_ab.py |
内容受控纯解码 A/B | 单流配置对比的唯一可用尺子:中位 + step_ms 一起看 |
报数必须写明用的是哪把尺子;方法学全貌见本系列关于测量方法学与补丁隔离的文章,本文只给"调 k 必须用哪把尺子"。
4.4 报数纪律
- 单流按区间 / 中位报 ,不得以单次自由生成的数字作承诺值。定稿配置实测区间 58.4~94.0 tok/s(内容受控 5 类 × 2 次)。
step_ms恒定,漂移由接受率解释------"按区间报"是这条指标的物理属性,不是保守措辞。- 定稿验收线:单流 80.0 tok/s (固定 prompt 自由生成,3 次方差 0)/ 71.3 tok/s (跨 5 类内容中位,区间 58.4~94.0),
step_ms33.8。
5. 暖机不足:"接受率提升"的假象
第一轮 MTP 头 A/B 测出"吞吐持平、接受率改善",当时被解读为"精度换草稿质量"。做充分暖机 的严格 A/B 后,两支结论完全相同 ,那个"接受率提升"是暖机不足的假象。
机制:torch.compile 按 shape 惰性编译,未暖机测到的包含编译时间(实测 conc=4 冷测 87.1 → 暖测 143.8,差 65%;TTFT 6.33 s → 0.163 s)。而接受率本身又是内容相关的(§3),两个效应叠加,极易把"这次文本更好猜 + 编译未完成"读成"新配置更好"。
由此固化的暖机协议(已硬编码进驱动脚本):
- 两轮暖机 :
batch 1~64各 128 token × 2 轮 + 一条 1024-token 长生成(覆盖所有惰性编译 shape)。 - 单流 5 次取中位 ;并发扫
1/4/8/16/32/64六档(曾在conc=8误断"已饱和",实测conc=64是其 2.8 倍)。 - 基准固定
reasoning_effort=low(非速度开关,但改变输出长度分布),思考档位写进记录。 - 每轮抓取
Mean acceptance length/Available KV cache memory/Model loading took/Graph capturing finished。 - 判定阈值 3%(5 次重复离散度约 ±4%,低于 3% 不作结论)。
⚠️ 并发档每档仅测一次,重复误差未被平均:conc 列 <5% 的差异按噪声处理 ;≥10% 且多档方向一致才可作结论------k=2 的高并发优势正是这种情形(
conc=8/32/64同向 +7.5%~16%)。
6. 跨会话漂移:判定阈值的依据
驱动脚本要求每轮带同会话对照(Phase A 在旧会话测),退回对照配置后再测一次(Phase A'),由此得到同配置、跨会话的漂移:
| 口径 | A(旧会话) | A'(新会话) | 漂移 |
|---|---|---|---|
| 单流 decode 中位 | 68.9 | 68.8 | −0.1% |
| conc=1 | 69.2 | 67.9 | −1.9% |
| conc=4 | 187.8 | 191.9 | +2.2% |
| conc=8 | 241.1 | 230.1 | −4.6% |
| conc=16 | 375.6 | 374.8 | −0.2% |
| conc=32 | 528.9 | 541.9 | +2.5% |
| conc=64 | 670.7 | 673.9 | +0.5% |
| 聚合总和 | 2073 | 2080 | +0.4% |
| KV | 17.07 GiB | 17.62 GiB | +3.2% |
后续三轮各复测一次,三次独立漂移:
| 轮次 | Δ单流中位 | Δ聚合总和 | KV A / A′ |
|---|---|---|---|
| 第 2 轮 | −0.1% | +0.3% | 17.07 / 17.62 |
| 第 4 轮 | +0.3% | +0.6% | 17.62 / 17.62 |
| 第 6 轮 | +1.6% | +0.2% | 17.62 / 17.62 |
阈值校准(直接决定 k 扫描的读法):
- 聚合总和漂移仅 +0.2%~+0.6% ⇒ 以聚合总和为主判据、3% 阈值,稳健。
- 单档 漂移可达 −4.6%(conc=8) ⇒ 单档差异 <5% 一律不作结论,须看聚合或做同会话 A/B。
- KV 余量 在同配置跨会话可差 3%(取决于
gpu_memory_utilization扣除不确定占用后的余项)⇒ 1~3% 的 KV 差异不作结论。
对应地:k=4 的 KV 少 1.4 GiB(16.27 vs 17.62,远超 3%)可以 当结论;k=1 的 17.83 vs 17.62 不能。
7. 踩坑与证否
① "最优 k 是常数"------错。 低并发下 k=3 最优(单流 70.6,k=1 −16.4%、k=2 −5.0%、k=4 −4.5%),但 conc=64 时 k=1 +15.0%、k=2 +15.7%。原因:把单流体验当成了唯一目标函数,而摊薄固定开销的收益只在 batch 小时存在------batch 一大,每步本已填满,多出的草稿只是白付验证成本。现做法:缺省 k=3,批量优先部署用 NUM_SPEC_TOKENS=2,记为显式决策点。
② "接受长度最高的 k=4 值得上"------错。 k=4 全面更差(单流 −4.5%、聚合 −5.2%)。原因:接受长度不是目标函数,k=3→4 多收 +0.5% 的 token、步进 +7.2%、净 −6%。现做法:调 k 时同看接受长度、step_ms、tok/step 三个量。
③ 用"固定 4-token prompt 自由生成"对比层切分,得出"重切分使单流掉 15%"(85.3 vs 72.5)------错。 内容受控后两支中位 71.2 vs 71.3,几乎无差;同一支内不同 prompt 可从 58.8 到 87.6。原因:点值测的是"这次文本好不好猜"(接受率 37.0%~93.1% 由内容决定)。现做法:单流配置对比一律走 scripts/decode_ab.py,看 step_ms + 跨内容中位;"单流按区间报"由工具强制执行。
④ 把"接受率提升"当作 MTP 头改进的证据------错。 充分暖机后严格 A/B 两支完全相同,提升是暖机不足假象。原因:惰性编译(冷测/暖测差 65%)与接受率的内容相关性叠加。现做法:两轮暖机 + 1024-token 长生成 + 单流 5 次取中位,硬编码进驱动脚本。
⑤ "参考项目 3.06 vs 我们 2.4~2.7,我们草稿质量差"------是口径问题。 接受长度负载相关:缺省思考档长篇 reasoning 可达 3.09/3.20,low 档简短答案仅 2.65。原因:拿自家"简短答案"对别家"长文本"。现做法:报接受长度必须同时报负载与思考档;跨配置比较用同一批内容受控 prompt。
⑥ 在 conc=8 断言"并发已饱和"------错。 conc=64 是其 2.8 倍。原因:并发曲线在低谷区会骗人(conc=12 曾低于 conc=8),单点采样看不出台阶。现做法:扫 6 档,主判据用聚合总和(漂移 +0.2%~+0.6%),单档 <5% 不认。
⑦ 一个至今没解释的异常,选择不解释。 k=1 在 conc=16 为 −8.6%,同配置其余高并发档为 +8~16%;排除捕获覆盖问题,超出跨会话漂移范围,未复测。记为单点异常------编一个听起来合理的归因,比标注"未验证"危害更大。
8. 复现命令
bash
# ---- 单流:纯解码基线(3 次重复,验确定性)----
python3 scripts/decode_speed.py --mode single -n 1024 --runs 3
# ---- prefill 无关性:证明朴素口径被污染 ----
python3 scripts/decode_speed.py --mode prompt-len -n 256 --lens 4,1024,4096,8192
# ---- 并发梯度(聚合 vs 每请求)----
python3 scripts/decode_speed.py --mode concurrency -n 512 --concurrency 1,2,4,8,16
# ---- 真实 chat 场景(思考档下的用户体感)----
python3 scripts/decode_speed.py --mode single -n 512 --runs 2 --endpoint chat
# ---- MTP 步进解剖:接受率 -> 每步产出 -> 解码速度(§1 恒等式的原始数据)----
python3 scripts/mtp_step_analysis.py --lens 4,1024,4096,8192,16384 -n 1024
# ---- 内容受控单流 A/B(调 k 或调层切分时必须使用)----
BASE_URL=http://127.0.0.1:8021 MODEL_NAME=glm-5.3-cybersec-w4a16 \
python3 scripts/decode_ab.py --label arm41_37 -n 512 --runs 2 \
--out experiments/pp_rebalance/decode_ab_4137.json
python3 scripts/decode_ab.py --compare a.json b.json
# ---- k 扫描:每个取值都要重启(捕获列表/编译缓存随之变化)----
NUM_SPEC_TOKENS=k ./deploy.sh start
python3 scripts/sweep_num_spec.py --values 3,4,6 # 脚本缺省 3,4,5
# ---- 引擎侧逐位接受率(判断"加深 k 是否还有价值")----
curl -s http://127.0.0.1:8021/metrics | grep -E 'spec_decode|Mean acceptance'