执行摘要
本 PR 修复了 Qwen3.8-27B cookbook 页面内嵌 mamba 缓存比例计算器(docs/src/snippets/_qwen38_mamba_ratio_calculator.jsx)在推算 --mamba-full-memory-ratio 与 --max-mamba-cache-size 时的 4 个行为偏差。该计算器派生自 Kimi K3 版本时丢失了 ReplaySSM、DSPARK、pin 公式与 S 槽位调制 4 项语义,导致所有 spec 配置下输出错误参数(RTX 5090 实测 ReplaySSM 场景 KV 池缩小 38%)。改动仅限文档站点的 2 个文件,不涉及服务端代码,但直接影响用户从页面复制部署参数的准确性。
功能与动机
PR body 明确指出计算器 was derived from the Kimi K3 one but lost four behaviours along the way, so it emitted the wrong --mamba-full-memory-ratio and --max-mamba-cache-size for every speculative configuration。四个缺陷分别是:
- 忽略
--enable-linear-replayssm-spec:ReplaySSM 把验证中间态放到固定 ring 上,D 应为 0,旧代码却按 D=4 计算,使 r 从真实的 1.04 变成 2.08,状态池 2 倍过度预留,挤压 KV 池。
- DSPARK 草稿数计算错误:DSPARK 不产生
--speculative-num-draft-tokens,验证窗口应为 --speculative-dspark-block-size(gamma)+ 1,旧代码 Number(null) || 4 得到 4,而实际是 8,导致状态池欠配。
- pin 重复计入 D:引擎只按 S 划分主状态池并单独分配 spec 缓冲,pin 应为
C * S。
- S 硬编码 5/4/3/1:忽略
SGLANG_OPT_MAMBA_SKIP_DECODE_LOCK 与 overlap/--pp-size 调制。
PR body 特别指出缺陷 1 与 2 误差方向相反,which is why no single spot-check surfaced them,这也是此前抽查未能发现问题的主要原因。
实现拆解
- 定位问题入口:核心改动集中在
_qwen38_mamba_ratio_calculator.jsx 的 derive() 函数,它是页面实时计算 --mamba-full-memory-ratio 与等价 pin 值的核心逻辑,输出会直接进入用户复制的部署命令。
- 重写 S 槽位计算:将硬编码 5/4/3/1 改为镜像服务端
kv_cache_configurator._calculate_mamba_ratio() 的表达式——基线 3,SGLANG_OPT_MAMBA_SKIP_DECODE_LOCK=1 时减 1,叠加 ping-pong track buffer(overlap 调度开启时 +2,extra_buffer_lazy 或 overlap 关闭时 +1),no_buffer 恒为 3,radix cache 禁用时 S 退化为 1。derive() 签名新增 env 参数以读取环境变量,调用处 eff 与 bs 分别传入所属命令的 env,避免 base 命令继承 Playground 的 overlay env。
- 重写 D 槽位计算:按 spec 算法分派——
--enable-linear-replayssm-spec 时 D 恒为 0(验证中间态移入固定 ring);DSPARK 时 D = --speculative-dspark-block-size + 1,缺省 fallback 为 7(RadixArk/Qwen3.8-27B-DSpark 默认 checkpoint 的 block_size);其他 EAGLE/MTP 场景沿用 --speculative-num-draft-tokens(缺省 4)。
- 修正 pin 公式与 env 传递:pin 从
C * (S + D) 改为 C * S,依据是引擎 kv_cache_configurator.py 中 mamba_cap = max_mamba_cache_size // _calculate_mamba_ratio() 只按 S 划分主状态池、spec 缓冲独立分配;base 命令 env 选择改为 cfg.baseFlags.length ? cfg.baseEnv : cfg.env,与 K3 计算器语义一致。
- 配套文档与验证:同步更新
Qwen3.8-27B.mdx 中 Accordion 的 S/D 语义、pin 公式与 DSPARK/ReplaySSM 说明,并重写 jsx 注释只描述当前语义。作者在 RTX 5090 上复现了 10 个 servable 配置的比率,并验证 no_buffer、radix-off、skip-lock、overlap-off 与显式 --speculative-dspark-block-size 7 各分支。
docs/src/snippets/_qwen38_mamba_ratio_calculator.jsx
核心改动文件,重写 derive() 的 S 与 D 计算、修正 pin 公式并新增 env 参数传递,直接影响用户从页面复制的部署参数准确性。
// 核心:derive() 从配置 flag 列表与运行环境推导缓存比例,语义对齐服务端
// kv_cache_configurator._calculate_mamba_ratio() 与 DSpark/EAGLE 的 spec 行为。
const derive = (flags, env) => {
const flagArg = (name) => {
for (const f of flags) {
const parts = f.split(/\s+/);
if (parts[0] === name) return parts[1];
}
return null;
};
const hasFlag = (name) => flags.some((f) => f.split(/[\s=]/)[0] === name);
// S = 每请求占用主状态池的槽位数(镜像引擎实现):
// 基线 3;SGLANG_OPT_MAMBA_SKIP_DECODE_LOCK=1 时减 1;
// 叠加 ping-pong track buffer:overlap 调度开启时为 2,
// extra_buffer_lazy 或 overlap 关闭时为 1,no_buffer 恒为 3,
// radix cache 禁用时 S 退化为 1。默认组合恰好等于旧硬编码 5/4/3/1。
const skipLock = env.some((e) =>
e.startsWith("SGLANG_OPT_MAMBA_SKIP_DECODE_LOCK=1"));
const overlapOff =
hasFlag("--disable-overlap-schedule") ||
(Number(flagArg("--pp-size")) || 1) > 1;
const slots = radixOff
? 1
: strategy === "no_buffer"
? 3
: 3 - (skipLock ? 1 : 0) +
(overlapOff || strategy === "extra_buffer_lazy" ? 1 : 2);
// D = 每条请求的 spec 验证中间状态数(不计入主池 pin)。
const algo = (flagArg("--speculative-algorithm") || "").toUpperCase();
// ReplaySSM 把验证中间态移到固定 ring 上,D 恒为 0。
const replaySpec = hasFlag("--enable-linear-replayssm-spec");
// DSPARK 不读 --speculative-num-draft-tokens:窗口 = gamma + 1,
// gamma 缺省时为 draft checkpoint 的 block_size(RadixArk 默认 7)。
const dsparkBlock = Number(flagArg("--speculative-dspark-block-size")) || 7;
const drafts = !specOn || replaySpec
? 0
: algo === "DSPARK"
? dsparkBlock + 1
: Number(flagArg("--speculative-num-draft-tokens")) || 4;
// 引擎只用 S 划分状态池:
// mamba_cap = max_mamba_cache_size // _calculate_mamba_ratio(),
// spec 缓冲独立分配,所以 pin = C x S 而非 C x (S + D)。
const ratio = ((slots + drafts) * stateBytesPerSlot) / (kvBytesPerToken * L);
return { ratio, slots, drafts, specOn /* 其余派生几何常量省略 */ };
};
// 两处调用:原命令与 base 命令各自携带自己的 env,
// base 命令的 env 不再继承 Playground overlay,语义与 K3 计算器一致。
const eff = derive(cfg.flags, cfg.env);
const bs = derive(
cfg.baseFlags.length ? cfg.baseFlags : cfg.flags,
cfg.baseFlags.length ? cfg.baseEnv : cfg.env,
);
const pin = Math.ceil(C * slots);
评论区精华
"If we fallback to the default DSpark checkpoint shouldn't it be 7 here?" —— zijiexia 指向默认 checkpoint 的 config.json block_size,Jiminator 以 checkpoint 配置、服务端 boot log 与 K3 一致性三重确认后修正为 7。
"There's no observable difference today — but the semantics are wrong, and this is the one line where the body claims K3 parity." —— zijiexia 抓住 env 选择逻辑与 K3 的唯一分歧点,Jiminator 随即修正。
"Can you also update the note in the mdx about how to calculating --max-mamba-cache-size? And please clean up the comments, some seems not accurate." —— zijiexia 的总体意见,推动文档与代码同步收敛。
风险与影响
风险:本次改动仅涉及文档站点文件,无运行时风险,但存在一致性维护风险——derive() 中的 S 表达式是服务端 _calculate_mamba_ratio() 的手工镜像,DSPARK 的 block_size=7 fallback 是硬编码默认值,若服务端逻辑或未来 checkpoint 的 block_size 变化,计算器需人工同步;当前无自动化测试或跨文件校验防止再次漂移。
影响:修复前用户从页面复制的参数在 spec 场景存在 2 倍偏差——ReplaySSM 场景 KV 池收缩 38%(19,469 → 11,992 tokens),DSPARK 场景状态池欠配(r=2.08 对 3.12)。修复后文档推荐配置与引擎实际划分一致,对长上下文与并发 >1 的部署尤其重要。团队维护成本体现为 jsx、mdx、服务端三处需保持一致。
关联脉络
该 PR 是 speculative-decoding 功能线在文档配套侧的收敛:上游 #34696 为 DSpark 增加 logprobs 支持、#35058 简化 spec logprob 接口,本 PR 则让 Qwen3.8-27B 的部署计算器与 DSpark/EAGLE worker 的实际行为(gamma 窗口、验证缓冲分配)对齐。其方法与 Kimi K3 计算器保持 parity 的约定,意味着未来新模型派生计算器时应以 K3 为基线并逐项核对 S/D 语义,避免再次出现同类丢失。
参与讨论