Skip to content

[Klaud Cold] Add GLM-5.2 NVFP4 GB300 agentic disaggregated dynamo-sglang recipe / 新增 GLM-5.2 NVFP4 GB300 智能体分离式 dynamo-sglang 配置#2299

Open
Oseltamivir wants to merge 3 commits into
mainfrom
klaud/glm52-fp4-gb300-sglang-mtp
Open

[Klaud Cold] Add GLM-5.2 NVFP4 GB300 agentic disaggregated dynamo-sglang recipe / 新增 GLM-5.2 NVFP4 GB300 智能体分离式 dynamo-sglang 配置#2299
Oseltamivir wants to merge 3 commits into
mainfrom
klaud/glm52-fp4-gb300-sglang-mtp

Conversation

@Oseltamivir

@Oseltamivir Oseltamivir commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds a GLM-5.2 NVFP4 agentic-coding disaggregated (prefill/decode) GB300 benchmark via dynamo-sglang, modeled on dsv4-fp4-gb300-dynamo-sglang-agentic-disagg (run 29385297092).

New config-key glm5.2-fp4-gb300-dynamo-sglang-agentic-disagg:

  • Agentic-coding scenario: EAGLE/MTP + DRAM hierarchical-cache KV offload + mooncake KV transfer, dedicated etcd/nats node, session-affinity dynamo frontend, custom agentic_srt.sh benchmark
  • Wide-EP decode: 1D TP16/EP16 attention-DP (deepep), fed by TP8/EP8 attention-DP prefill workers scaling 1→6
  • Arms: p1x8_d1x4 conc 8, p1x8/p2x8_d1x16 conc 256, p3x8_d1x16 conc 512, p6x8_d1x16 conc 1024 (3/6/8/10/16 nodes)

This branch evolved through the review comments: single-node → disagg → wide-EP decode → agentic (this). Earlier revisions are superseded.

Changes

  • benchmarks/multi_node/srt-slurm-recipes/sglang/glm5.2/gb300-fp4/agentic/*.yaml — 5 new recipes (DSv4 agentic disagg structure + GLM-5.2 serve flags: glm47/glm45 parsers, deepep, hicache-ratio 0.75, SGLANG_TIMEOUT_KEEP_ALIVE=900 for the AIPerf warmup race)
  • configs/nvidia-master.yamlglm5.2-fp4-gb300-dynamo-sglang-agentic-disagg (runner: cluster:gb300-nv, mooncake, dynamo-router 1.3.0.dev1)
  • configs/runners.yamlnew cluster:gb300-nv hardware entry (available-cpu-dram-mib/gpus-per-node) required by the agentic DRAM-offload sizing; also unblocks the existing DSv4 gb300 agentic configs (previously errored full config-gen)
  • runners/launch_gb300-nv.shIS_AGENTIC glm5.2 dynamo-sglang branch (srt-slurm v1.0.10) + glm5.2 model-path (shared-NFS /data shim)
  • perf-changelog.yaml — trigger entry

Image / weights / dynamo

  • Image lmsysorg/sglang:v0.5.15.post1-cu130 (GLM-5.2-validated on-node). Dynamo 1.3.0.dev1 — ≥1.2.1 is required because older dynamo's minijinja PromptFormatter rejects GLM-5.2's chat-template .0 dot-index (verified against the earlier disagg run's frontend log).
  • nvidia/GLM-5.2-NVFP4 staged on shared NFS at /data/home/sa-shared/models/GLM-5.2-NVFP4.

中文说明

新增 GLM-5.2 NVFP4 智能体(agentic)分离式(预填充/解码)GB300 基准测试(dynamo-sglang),参照 dsv4-fp4-gb300-dynamo-sglang-agentic-disagg(run 29385297092)。

新增配置键 glm5.2-fp4-gb300-dynamo-sglang-agentic-disagg

  • agentic-coding 场景:EAGLE/MTP + DRAM 分层缓存 KV 卸载 + mooncake KV 传输、专用 etcd/nats 节点、会话亲和 dynamo 前端、自定义 agentic_srt.sh 基准
  • 宽 EP 解码:1D TP16/EP16 注意力 DPdeepep),由 TP8/EP8 注意力 DP 预填充 worker(1→6)驱动
  • 各拓扑并发 8/256/256/512/1024(3/6/8/10/16 节点)

本分支随评审逐步演进:单节点 → 分离式 → 宽 EP 解码 → agentic(当前)。早先版本已被取代。

改动

  • 新增 5 个配方 YAML(DSv4 智能体分离式结构 + GLM-5.2 服务标志:glm47/glm45 解析器、deepephicache-ratio 0.75SGLANG_TIMEOUT_KEEP_ALIVE=900
  • configs/nvidia-master.yaml 新增 glm5.2-fp4-gb300-dynamo-sglang-agentic-disaggrunner: cluster:gb300-nv、mooncake、dynamo-router 1.3.0.dev1
  • configs/runners.yaml 新增 cluster:gb300-nv 硬件条目(智能体 DRAM 卸载所需;同时修复此前报错的 DSv4 gb300 智能体配置的全量配置生成)
  • runners/launch_gb300-nv.sh 新增 IS_AGENTIC glm5.2 dynamo-sglang 分支(srt-slurm v1.0.10)+ glm5.2 模型路径(共享 NFS /data 垫层)
  • perf-changelog.yaml 触发项

镜像 / 权重 / dynamo

  • 镜像 lmsysorg/sglang:v0.5.15.post1-cu130(已在节点验证 GLM-5.2 NVFP4)。Dynamo 1.3.0.dev1——需 ≥1.2.1,否则旧版 dynamo 的 minijinja 无法解析 GLM-5.2 聊天模板的 .0 点索引。
  • nvidia/GLM-5.2-NVFP4 暂存于共享 NFS /data/home/sa-shared/models/GLM-5.2-NVFP4

🤖 Generated with Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

Thanks for the contribution! Please reach out to respective companies' CODEOWNER to fill in the latest PR_REVIEW_CHECKLIST.md before pinging core maintainer on Slack for review. In order for the signoff PR check bot to trigger, you must follow the PR_REVIEW_CHECKLIST.md template correctly, including the phrase As a PR reviewer and CODEOWNER, I have reviewed this and have.

For PR verification, add the full-sweep-fail-fast label (strongly recommended) to this PR — the benchmark sweep only runs on labeled PRs. Use full-sweep-enabled only if you need matrix jobs to keep running past a failure.

PR authors are responsible for ensuring that after merging, all GitHub Action jobs fully pass. A lot of the time, failures are just flakes and simply re-running the failed jobs will fix it. See GitHub's docs on re-running failed jobs


感谢你的贡献!请联系相应公司的 CODEOWNER 填写最新的 PR_REVIEW_CHECKLIST.md,然后再在 Slack 上联系核心维护者进行审阅。为了触发 signoff PR 检查机器人,你必须正确遵循 PR_REVIEW_CHECKLIST.md 模板,包括保留英文语句 As a PR reviewer and CODEOWNER, I have reviewed this and have

如需进行 PR 验证,请为此 PR 添加 full-sweep-fail-fast 标签(强烈推荐)— 基准测试 sweep 仅在带有标签的 PR 上运行。仅当需要矩阵任务在失败后继续运行时才使用 full-sweep-enabled

PR 作者有责任确保合并后所有 GitHub Action 任务完全通过。 很多时候失败只是偶发抖动(flake),重新运行失败的任务即可解决。参见 GitHub 关于重新运行失败任务的文档

Oseltamivir added a commit that referenced this pull request Jul 22, 2026
中文:将 perf-changelog 的 pr-link 更新为 #2299。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

@github-actions

Copy link
Copy Markdown
Contributor

@Oseltamivir

Copy link
Copy Markdown
Collaborator Author

full-sweep-fail-fast greenrun 29904819795. All 5 concurrency points (c1 canary + c2/c4/c8/c16) and the gsm8k eval passed on GB300 (TP4, 8k1k).

conc tput/gpu (tok/s) out/gpu (tok/s)
1 689 77
2 1021 115
4 1574 175
8 2225 250
16 3045 337
  • gsm8k em_strict = 0.964 (default floor 0.90; cf. glm5 0.94, glm5.1 0.93) — NVFP4+MTP preserves accuracy.
  • MTP confirmed active: accept length ~5.15 (on-node), TPOT ~3.9 ms at conc 2.
  • The first sweep failed only on a port conflict — GB300 compute nodes run a host service on 127.0.0.1:8888 and enroot shares the host network namespace, so sglang's 0.0.0.0:8888 bind failed after a full load. Fixed by serving the single-node gb300 path on port 40000 (b300-nv has no such service, so launch_b300-nv.sh can keep 8888).

中文:full-sweep-fail-fast 全绿(run 29904819795)。GB300(TP4、8k1k)上全部 5 个并发点(c1 金丝雀 + c2/c4/c8/c16)及 gsm8k 评估均通过。吞吐见上表(tok/s/卡)。gsm8k em_strict = 0.964(默认下限 0.90;glm5 为 0.94、glm5.1 为 0.93),NVFP4+MTP 未损失精度;MTP 已生效(节点验证接受长度约 5.15,并发 2 时 TPOT 约 3.9 ms)。首次扫描仅因端口冲突失败——GB300 计算节点在 127.0.0.1:8888 运行宿主服务,而 enroot 共享宿主网络命名空间,导致 sglang 绑定 0.0.0.0:8888 在完整加载后失败;已改为在 gb300 单节点路径使用 40000 端口修复(b300-nv 无此服务,故 launch_b300-nv.sh 可继续用 8888)。

@Oseltamivir
Oseltamivir force-pushed the klaud/glm52-fp4-gb300-sglang-mtp branch from e7e0a78 to d6a23b0 Compare July 22, 2026 10:21
@Oseltamivir Oseltamivir changed the title [Klaud Cold] Add GLM-5.2 NVFP4 GB300 single-node SGLang low-latency (MTP) recipe / 新增 GLM-5.2 NVFP4 GB300 单节点 SGLang 低延迟(MTP)配置 [Klaud Cold] Add GLM-5.2 NVFP4 GB300 disaggregated dynamo-sglang recipe / 新增 GLM-5.2 NVFP4 GB300 分离式 dynamo-sglang 配置 Jul 22, 2026
@github-actions

Copy link
Copy Markdown
Contributor

@github-actions

Copy link
Copy Markdown
Contributor

@Oseltamivir
Oseltamivir force-pushed the klaud/glm52-fp4-gb300-sglang-mtp branch from d6a23b0 to c3a1b08 Compare July 22, 2026 10:51
@Oseltamivir Oseltamivir changed the title [Klaud Cold] Add GLM-5.2 NVFP4 GB300 disaggregated dynamo-sglang recipe / 新增 GLM-5.2 NVFP4 GB300 分离式 dynamo-sglang 配置 [Klaud Cold] Add GLM-5.2 NVFP4 GB300 disaggregated wide-EP dynamo-sglang recipe / 新增 GLM-5.2 NVFP4 GB300 分离式宽 EP dynamo-sglang 配置 Jul 22, 2026
@Oseltamivir
Oseltamivir force-pushed the klaud/glm52-fp4-gb300-sglang-mtp branch from c3a1b08 to 2bf8101 Compare July 22, 2026 11:20
@github-actions

Copy link
Copy Markdown
Contributor

@Oseltamivir
Oseltamivir force-pushed the klaud/glm52-fp4-gb300-sglang-mtp branch from 2bf8101 to fd36093 Compare July 22, 2026 12:42
@Oseltamivir Oseltamivir changed the title [Klaud Cold] Add GLM-5.2 NVFP4 GB300 disaggregated wide-EP dynamo-sglang recipe / 新增 GLM-5.2 NVFP4 GB300 分离式宽 EP dynamo-sglang 配置 [Klaud Cold] Add GLM-5.2 NVFP4 GB300 agentic disaggregated dynamo-sglang recipe / 新增 GLM-5.2 NVFP4 GB300 智能体分离式 dynamo-sglang 配置 Jul 22, 2026
@github-actions

Copy link
Copy Markdown
Contributor

@Oseltamivir
Oseltamivir force-pushed the klaud/glm52-fp4-gb300-sglang-mtp branch from fd36093 to 225c439 Compare July 22, 2026 12:55
@github-actions

Copy link
Copy Markdown
Contributor

@Oseltamivir
Oseltamivir force-pushed the klaud/glm52-fp4-gb300-sglang-mtp branch from 225c439 to 1f7c557 Compare July 22, 2026 13:19
@github-actions

Copy link
Copy Markdown
Contributor

@Oseltamivir
Oseltamivir force-pushed the klaud/glm52-fp4-gb300-sglang-mtp branch from 1f7c557 to a10a67b Compare July 22, 2026 13:33
@github-actions

Copy link
Copy Markdown
Contributor

Agentic-coding disaggregated (prefill/decode) GB300 sweep, modeled on
dsv4-fp4-gb300-dynamo-sglang-agentic-disagg (run 29385297092): EAGLE/MTP + DRAM
hierarchical-cache KV offload, mooncake KV transfer, dedicated etcd/nats node +
session-affinity dynamo frontend, custom agentic_srt.sh benchmark. Wide-EP decode
(1D TP16/EP16 attention-DP) fed by TP8/EP8 attention-DP prefill workers scaling
1->6; arms conc 8 (D TP4), 256, 256, 512, 1024 (3/6/8/10/16 nodes).

GLM-5.2 serve flags (glm47/glm45 parsers, deepep, hicache-ratio 0.75,
SGLANG_TIMEOUT_KEEP_ALIVE=900 for the AIPerf warmup race) grafted onto the DSv4
agentic disagg machinery. Image v0.5.15.post1-cu130, dynamo 1.3.0.dev1 (>=1.2.1
needed for GLM-5.2's chat-template `.0` dot-index).

- Recipes benchmarks/multi_node/srt-slurm-recipes/sglang/glm5.2/gb300-fp4/agentic/*.yaml
- configs/nvidia-master.yaml: glm5.2-fp4-gb300-dynamo-sglang-agentic-disagg (runner cluster:gb300-nv, mooncake, dynamo-router 1.3.0.dev1)
- configs/runners.yaml: add cluster:gb300-nv hardware entry (available-cpu-dram-mib/gpus-per-node) required by agentic DRAM-offload sizing; also unblocks the existing DSv4 gb300 agentic configs
- launch_gb300-nv.sh: IS_AGENTIC glm5.2 dynamo-sglang branch (srt-slurm v1.0.10) + glm5.2 model-path (/data shared-NFS shim)
- perf-changelog.yaml trigger entry

中文:GB300 智能体(agentic)分离式(预填充/解码)dynamo-sglang 配方,参照
dsv4-fp4-gb300-dynamo-sglang-agentic-disagg(run 29385297092):EAGLE/MTP + DRAM 分层缓存
KV 卸载、mooncake KV 传输、专用 etcd/nats 节点 + 会话亲和 dynamo 前端、自定义 agentic_srt.sh
基准。宽 EP 解码(1D TP16/EP16 注意力 DP),由 TP8/EP8 注意力 DP 预填充 worker(1→6)驱动。
GLM-5.2 服务标志(glm47/glm45 解析器、deepep、hicache-ratio 0.75)叠加于 DSv4 智能体分离式
机制之上。新增 cluster:gb300-nv 硬件条目(智能体 DRAM 卸载所需,同时修复现有 DSv4 配置)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Oseltamivir
Oseltamivir force-pushed the klaud/glm52-fp4-gb300-sglang-mtp branch from a10a67b to 14593e3 Compare July 22, 2026 13:50
@github-actions

Copy link
Copy Markdown
Contributor

Resolve perf-changelog.yaml by taking main's bytes verbatim and re-appending
this PR's glm5.2-fp4-gb300-dynamo-sglang-agentic-disagg entry at the tail
(KLAUD_DEBUG.md 1.1) -- a 3-way merge silently rewrites whitespace and
re-trips the changelog deletion check.

中文:将 origin/main 合并入本分支。perf-changelog.yaml 按 KLAUD_DEBUG.md 1.1
处理:直接取 main 的原始字节,再把本 PR 的
glm5.2-fp4-gb300-dynamo-sglang-agentic-disagg 条目追加到文件末尾——三方合并
会静默改动空白字符并再次触发删除检查。
…v0.5.16

Every prefill worker in the GB300 agentic disagg sweep died on its first
forward (run 29925753943):

  File "sglang/srt/layers/moe/moe_runner/flashinfer_cutedsl.py", line 476,
    in fused_experts_deepep_to_flashinfer_cutedsl_fp4
      hidden_states, hidden_states_scale, _, _, masked_m, _ = dispatch_output
  ValueError: not enough values to unpack (expected 6, got 5)

The only ("deepep", *) NVFP4 MoE fused func SGLang registers is
flashinfer_cutedsl's, and it consumes the 6-field low-latency
DeepEPLLDispatchOutput. `deepep-mode: auto` resolves to `normal` on every
extend batch (DeepEPMode.resolve), which yields the 5-field
DeepEPNormalDispatchOutput -- so a prefill-role engine can never survive
deepep + NVFP4. `deepep-mode: low_latency` is not a way out either: DeepEP
asserts num_max_dispatch_tokens_per_rank <= 1024 while chunked prefill puts
32768/dp8 = 4096 tokens on a rank. This is unchanged in v0.5.16, so it is a
recipe bug, not an image bug.

Both roles move to the MoE backends the proven in-repo NVFP4 recipes use:

  prefill      no EP a2a + moe-runner-backend flashinfer_trtllm
               (qwen3.5/gb300-fp4 maxtpt, dsr1/b200-fp4 1k1k disagg mtp)
  wide-EP      moe-a2a-backend flashinfer (MNNVL) + flashinfer_cutedsl
  decode       (qwen3.5/gb300-fp4 maxtpt). The ("flashinfer", cutedsl) fused
               func takes a StandardDispatchOutput, so unlike the deepep one
               it also survives MTP's extend-shaped target-verify batch.

ep-num-redundant-experts / ep-dispatch-algorithm are dropped along with
DeepEP: the FlashInfer a2a dispatcher takes num_local_experts as "Unused" and
plumbs no expert map, so they had no effect on this path. Worth revisiting as
a tuning follow-up. SGLANG_DEEPEP_NUM_MAX_DISPATCH_TOKENS_PER_RANK is replaced
by SGLANG_FLASHINFER_NUM_MAX_DISPATCH_TOKENS_PER_RANK on the decode role,
which sizes the a2a recv buffer (peak 3072/16 * 4 = 768 tokens/rank).

Image bumped v0.5.15.post1-cu130 -> v0.5.16-cu130 in the same pass.

A second, separate failure surfaced once prefill was fixed: the decode engine
died at init with "Pre-permute function for flashinfer to triton is not
registered". GLM-5.2-NVFP4 leaves the MTP/nextn layer unquantized
(hf_quant_config excludes `model.layers.78*`), so the EAGLE draft's MoE is bf16
and UnquantizedFusedMoEMethod pins it to the triton runner core; inheriting the
target model's FlashInfer a2a then has no ("flashinfer","triton") permute.
SGLang already handles this in _deepseek_spec_moe_resolution ("use triton fused
moe by default for bf16 nextn layer in deepseek fp4 checkpoint") but gates the
hook on is_hip(), so on CUDA the draft silently inherits. The wide-EP decode
now sets upstream's own ROCm values explicitly:
speculative-moe-a2a-backend none + speculative-moe-runner-backend triton.

Validated in real disaggregation on GB300 batch_1 before CI, via a replica of
runners/launch_gb300-nv.sh (srt-slurm v1.0.10, srtctl apply, mooncake KV
transfer, dedicated etcd/nats node, session-affinity dynamo frontend) on the
2p4d c256 arm -- the smallest arm exercising both changed roles:

  job 17402  deepep prefill               -> died, but on the NEW decode-side
                                             Pre-permute assert; the original
                                             unpack ValueError was already gone
  job 17430  this recipe, 2P x 4D         -> "Server is healthy - starting
                                             benchmark" (1P + 1D up in 9m10s)
  job 17431  trtllm-decode fallback       -> also healthy; kept as the
                                             documented alternative

Both jobs then failed only in the benchmark step, because the out-of-CI replica
does not export KV_OFFLOAD_BACKEND (CI sets it from the master config's
kv-offload-backend). Engine bring-up is what was under test and it passed. The
c8 arm's TP4/EP1 decode was not run on-node; it is unaffected by both changes
(a2a none on both roles) and its prefill is identical to the arms that were.

中文:GB300 智能体分离式扫描中每个预填充(prefill) worker 在首次前向传播即崩溃
(run 29925753943)。SGLang 注册的唯一 ("deepep", *) NVFP4 混合专家(MoE)融合
函数来自 flashinfer_cutedsl,它消费 6 字段的低延迟 DeepEPLLDispatchOutput;而
`deepep-mode: auto` 在任何 extend 批次上都解析为 `normal`,产出 5 字段的
DeepEPNormalDispatchOutput,因此预填充角色在 deepep + NVFP4 下不可能正常工作。
改用 low_latency 也走不通:DeepEP 断言 num_max_dispatch_tokens_per_rank <= 1024,
而分块预填充在单 rank 上会放 32768/dp8 = 4096 个 token。v0.5.16 中该逻辑未变,
所以这是配置问题而非镜像问题。

两个角色改用仓库内已验证的 NVFP4 配方所用的 MoE 后端:预填充不使用 EP
all-to-all,改为 moe-runner-backend flashinfer_trtllm;宽 EP 解码使用
moe-a2a-backend flashinfer(MNNVL)+ flashinfer_cutedsl——该融合函数接收
StandardDispatchOutput,因此也能承受 MTP 的 extend 形状 target-verify 批次。
ep-num-redundant-experts / ep-dispatch-algorithm 随 DeepEP 一并移除(FlashInfer
a2a 分发器不传递专家映射,这两个参数在此路径上本就无效),可作为后续调优项。
镜像同步升级至 v0.5.16-cu130。预填充修复后暴露出第二个独立问题:解码引擎在初始化阶段崩溃,报
"Pre-permute function for flashinfer to triton is not registered"。
GLM-5.2-NVFP4 未量化 MTP/nextn 层(hf_quant_config 排除了 `model.layers.78*`),
因此 EAGLE 草稿模型的 MoE 为 bf16,UnquantizedFusedMoEMethod 会强制其使用 triton
runner;此时若沿用目标模型的 FlashInfer a2a,则不存在 ("flashinfer","triton")
的 pre-permute 函数。SGLang 在 _deepseek_spec_moe_resolution 中已处理该情况
("对 fp4 检查点中的 bf16 nextn 层默认使用 triton fused moe"),但该钩子被
is_hip() 限制,故在 CUDA 上草稿模型会静默继承目标配置。现已在宽 EP 解码中显式
设置上游在 ROCm 下使用的同一组取值:speculative-moe-a2a-backend none 与
speculative-moe-runner-backend triton。

上线 CI 前已在 GB300(batch_1)以真实分离式部署验证:复刻
runners/launch_gb300-nv.sh(srt-slurm v1.0.10、srtctl apply、mooncake KV 传输、
专用 etcd/nats 节点、会话亲和 dynamo 前端),运行 2p4d c256 这一同时覆盖两个角色
改动的最小拓扑。job 17430(本配方,2P x 4D)达到 "Server is healthy - starting
benchmark"(1P + 1D 在 9 分 10 秒内就绪);job 17431(trtllm 解码回退方案)同样
健康,作为备选方案记录在案。两个作业随后仅在基准测试步骤失败,原因是 CI 之外的
复刻环境未导出 KV_OFFLOAD_BACKEND(CI 由主配置的 kv-offload-backend 提供)——本次
验证的目标是引擎启动,该目标已通过。c8 拓扑的 TP4/EP1 解码未在节点上实测:它不受
本次两项改动影响(两个角色均为 a2a none),且其预填充配置与已验证的拓扑完全一致。
@Oseltamivir

Copy link
Copy Markdown
Collaborator Author

Fixed the prefill crash from run 29925753943, rebased, image → SGLang v0.5.16

Root cause: DeepEP cannot serve NVFP4 outside low-latency decode

Every prefill worker died on its first forward:

File "sglang/srt/layers/moe/moe_runner/flashinfer_cutedsl.py", line 476,
  in fused_experts_deepep_to_flashinfer_cutedsl_fp4
    hidden_states, hidden_states_scale, _, _, masked_m, _ = dispatch_output
ValueError: not enough values to unpack (expected 6, got 5)

The only ("deepep", *) NVFP4 MoE fused func SGLang registers is
flashinfer_cutedsl's, and it consumes the 6-field DeepEPLLDispatchOutput
(low-latency / masked dispatch). deepep-mode: auto resolves to normal on
every extend batch (DeepEPMode.resolve(is_extend_in_batch=True)), which yields
the 5-field DeepEPNormalDispatchOutput — so a prefill-role engine can never
survive deepep + NVFP4. deepep-mode: low_latency is not a way out either:
DeepEP asserts num_max_dispatch_tokens_per_rank <= 1024 while chunked prefill
puts 32768/dp8 = 4096 tokens on a rank.

This is a recipe bug, not an image bug — the unpack is byte-identical in
v0.5.16, so the image bump alone would not have fixed it.

Fix: the MoE backends the proven in-repo NVFP4 recipes use

Role Before After Precedent
prefill deepep + flashinfer_cutedsl no EP a2a + flashinfer_trtllm qwen3.5/gb300-fp4 maxtpt, dsr1/b200-fp4 1k1k disagg mtp
wide-EP decode deepep + flashinfer_cutedsl flashinfer (MNNVL) a2a + flashinfer_cutedsl qwen3.5/gb300-fp4 maxtpt
c8 decode (TP4/EP1) a2a none + flashinfer_cutedsl unchanged

The ("flashinfer", flashinfer_cutedsl) fused func takes a
StandardDispatchOutput, so unlike the deepep one it also survives MTP's
extend-shaped target-verify batch on the decode engine. server_args requires
dp_size == tp_size + attention-DP and ep_size == tp_size for FlashInfer a2a —
all true here (16/16/16).

Also dropped with DeepEP: ep-num-redundant-experts / ep-dispatch-algorithm
(the FlashInfer a2a dispatcher takes num_local_experts as "Unused" and plumbs
no expert map, so they had no effect on this path — worth revisiting as a tuning
follow-up), and SGLANG_DEEPEP_NUM_MAX_DISPATCH_TOKENS_PER_RANK, replaced on the
decode role by SGLANG_FLASHINFER_NUM_MAX_DISPATCH_TOKENS_PER_RANK which sizes
the a2a recv buffer (peak 3072/16 * 4 = 768 tokens/rank).

Second root cause (surfaced only after the first was fixed)

With prefill fixed, the decode engine then died at init with:

AssertionError: Pre-permute function for flashinfer to triton is not registered
  ... sglang/srt/layers/quantization/unquant.py, line 641

GLM-5.2-NVFP4 leaves the MTP/nextn layer unquantizedhf_quant_config.json
excludes model.layers.78* — so the EAGLE draft's MoE is bf16 and
UnquantizedFusedMoEMethod pins it to the triton runner core regardless of
moe-runner-backend. Left to inherit the target model's FlashInfer a2a, there is
no ("flashinfer","triton") pre-permute → dead at init.

SGLang already handles exactly this — _deepseek_spec_moe_resolution, "use triton
fused moe by default for bf16 nextn layer in deepseek fp4 checkpoint"
— but the
hook is gated on is_hip(), so on CUDA the draft silently inherits instead. The
wide-EP decode now sets upstream's own ROCm values explicitly:

speculative-moe-a2a-backend: none
speculative-moe-runner-backend: triton

Why the qwen3.5/gb300-fp4 precedent did not cover this: those arms are STP.
FlashInfer a2a + MTP was an untested combination in this repo.

Image bump

lmsysorg/sglang:v0.5.15.post1-cu130lmsysorg/sglang:v0.5.16-cu130.
The arm64 enroot squash is already imported on the GB300 cluster, so the sweep
reuses it rather than re-importing ~25 GB.

Rebase

The PR was CONFLICTING, so run-sweep.yml would not start. Resolved per
KLAUD_DEBUG.md §1.1: took main's perf-changelog.yaml bytes verbatim, then
re-appended this PR's own entry at the tail (verified byte-exact append — no
3-way merge, no whitespace rewrite).

On-node validation

Verified in real disaggregation (no aggregated stand-in) on GB300 batch_1,
through a replica of runners/launch_gb300-nv.sh — srt-slurm v1.0.10,
srtctl apply, mooncake KV transfer, dedicated etcd/nats node, session-affinity
dynamo frontend, v0.5.16 squash — on the 2p4d c256 arm, the smallest arm that
exercises both changed roles at once:

job config outcome
17402 original deepep prefill died — but on the new decode-side Pre-permute assert; the original unpack ValueError was already gone
17430 this recipe, 2P (TP8/EP8/DPA) × 4D (TP16/EP16/DPA) Server is healthy - starting benchmark — 1P + 1D up in 9m10s
17431 flashinfer_trtllm decode fallback also healthy; kept as the documented alternative

Both then failed only in the benchmark step, because the out-of-CI replica does
not export KV_OFFLOAD_BACKEND (CI supplies it from the master config's
kv-offload-backend) — Error: KV_OFFLOAD_BACKEND is required when KV_OFFLOADING=dram.
Engine bring-up was what was under test, and it passed.

Not covered on-node: the c8 arm's TP4/EP1 decode (unaffected by both changes —
a2a none on both roles — and its prefill is identical to the arms that were run),
and throughput, which this full sweep produces.

中文说明

根因:DeepEP 无法在低延迟解码之外服务 NVFP4

run 29925753943 中每个预填充(prefill) worker 在首次前向传播即崩溃,报
ValueError: not enough values to unpack (expected 6, got 5)。SGLang 注册的唯一
("deepep", *) NVFP4 混合专家(MoE)融合函数来自 flashinfer_cutedsl,它消费
6 字段DeepEPLLDispatchOutput(低延迟/掩码式分发);而 deepep-mode: auto
在任何 extend 批次上都解析为 normal,产出 5 字段
DeepEPNormalDispatchOutput——因此预填充角色在 deepep + NVFP4 下不可能正常工作。
改用 low_latency 也走不通:DeepEP 断言
num_max_dispatch_tokens_per_rank <= 1024,而分块预填充在单 rank 上会放
32768/dp8 = 4096 个 token。这是配置问题而非镜像问题——v0.5.16 中该解包逻辑
完全未变,仅升级镜像并不能修复。

修复:改用仓库内已验证 NVFP4 配方所用的 MoE 后端

  • 预填充:不使用 EP all-to-all,改为 moe-runner-backend: flashinfer_trtllm
    (参照 qwen3.5/gb300-fp4 maxtpt 与 dsr1/b200-fp4 1k1k 分离式 mtp)
  • 宽 EP 解码moe-a2a-backend: flashinfer(MNNVL)+ flashinfer_cutedsl
    (参照 qwen3.5/gb300-fp4 maxtpt)。该融合函数接收 StandardDispatchOutput
    因此也能承受 MTP 在解码引擎上运行的 extend 形状 target-verify 批次。
    server_args 要求 FlashInfer a2a 满足 dp_size == tp_size + 注意力 DP 且
    ep_size == tp_size,此处(16/16/16)均满足。
  • c8 低并发解码(TP4/EP1)保持不变。

ep-num-redundant-experts / ep-dispatch-algorithm 随 DeepEP 一并移除
(FlashInfer a2a 分发器不传递专家映射,这两个参数在此路径上本就无效,可作为后续
调优项);SGLANG_DEEPEP_NUM_MAX_DISPATCH_TOKENS_PER_RANK 在解码角色上替换为
SGLANG_FLASHINFER_NUM_MAX_DISPATCH_TOKENS_PER_RANK(用于设定 a2a 接收缓冲区
大小,峰值 3072/16 * 4 = 768 token/rank)。

镜像升级

镜像由 v0.5.15.post1-cu130 升级至 v0.5.16-cu130;arm64 enroot squash 已在
GB300 集群导入,扫描将直接复用而无需重新导入约 25 GB。

变基

此前 PR 处于 CONFLICTING 状态,run-sweep.yml 不会启动。已按
KLAUD_DEBUG.md §1.1 处理:直接取 main 的 perf-changelog.yaml 原始字节,再把本
PR 自身的条目追加到末尾(已校验为字节级纯追加,未做三方合并,未改动空白字符)。

节点验证

已在 GB300 batch_1 上以真实分离式部署验证(未使用聚合式替代方案):复刻
runners/launch_gb300-nv.sh(srt-slurm v1.0.10srtctl apply、mooncake KV 传输、
专用 etcd/nats 节点、会话亲和 dynamo 前端、v0.5.16 squash),运行 2p4d c256——
同时覆盖两个改动角色的最小拓扑。

job 17430(本配方,2P TP8/EP8/DPA × 4D TP16/EP16/DPA)达到
Server is healthy - starting benchmark,1P + 1D 在 9 分 10 秒内就绪;
job 17431(flashinfer_trtllm 解码回退方案)同样健康,作为备选方案记录在案。
两者随后仅在基准测试步骤失败,因为 CI 之外的复刻环境未导出 KV_OFFLOAD_BACKEND
(CI 由主配置的 kv-offload-backend 提供);本次验证目标为引擎启动,已通过。

未在节点上覆盖的部分:c8 拓扑的 TP4/EP1 解码(不受本次两项改动影响——两个角色
均为 a2a none,且其预填充与已验证拓扑完全一致),以及吞吐量数据(由本次完整
扫描产出)。

@github-actions

Copy link
Copy Markdown
Contributor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

1 participant