Repository navigation
feat(serving): SGLang draft classes for the Qwen3.8-27B DSpark MoE drafter (SGLang FusedMoE path) + tuned fused-MoE configs - #941
Conversation
…after (Kan's MoE drafters) + tuned fused-MoE configs SGLang cannot load SpecForge's MoE-FFN drafters (the Qwen3.8-27B DSpark MoE exports of sgl-project#811/sgl-project#812 and kan/moe-3-qwen38, e.g. RadixArk/qwen38-dspark-moe-3ep-cont-step9916). This adds `specforge.serving.sglang_models`, an SGLANG_EXTERNAL_MODEL_PACKAGE with `Qwen3MoeDSparkModel` (plus DFlash/DFlash2 MoE variants): SGLang's own `Qwen3DSparkModel` with every layer's MLP replaced by SGLang's own MoE block (`TopK` + `FusedMoE` + sigmoid-gated shared expert, as in `Qwen2MoeSparseMoeBlock`). Weights load through the modules' own weight loaders (`FusedMoE.make_expert_params_mapping` for the per-expert tensors), so expert-width padding, runner-specific layouts (Triton / flashinfer TRT-LLM), quantised checkpoints and TP sharding stay SGLang's business; a strict check refuses partially matching exports. Router logits are computed in fp32 with the folded centering bias exactly as the trainer does, so routing matches training; both the Qwen and the DeepSeek checkpoint naming are accepted. Also ships the tuned fused-MoE Triton configs for this drafter's expert shape (512 x 512, top-10) for H200 and GB300 under patches/sglang/moe_configs, with the stand-in config and a wrapper for SGLang's tuner (SGLang has no config for E=512,N=512 and runs the kernel untuned otherwise), plus the equivalence gate (served block vs plain-PyTorch reference, identical routing, bf16-level drift, truncated export refused; passes on H200 and GB300 for the Triton and the TRT-LLM runner) and CPU unit tests. Measured (bf16 target Qwen/Qwen3.8-27B, FP8-free, gsm8k/mt-bench, c=1/8/32): on GB300 the MoE drafter now matches the official dense drafter at c=1/8 and trails 5-10% at c=32 with 1-2% higher accept length; tuned config +0.4-1.8%, TRT-LLM runner +1-3%. The standard loader is as fast as a direct stacked copy end to end (GB300 +0.4-2.5%, H200 within noise) and loads in 8 s (GB300) / 38 s (H200). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Follow-up: if need to support - DeepSeek-V4 MoE drafters (#812) sgl-project/SpecForge#941 serves the Qwen recipe only. The DSV4-Flash drafter (64 x 2048 experts, top-6, ungated shared expert, sqrtsoftplus scoring, noaux_tc selection bias, scaling 1.5, SwiGLU clamp 10) needs: Routing: build TopK with scoring_func="sqrtsoftplus", correction_bias= the export's fp32 gate.bias (selection only, not added to logits), use_grouped_topk / num_expert_group / topk_group, routed_scaling_factor=1.5. SGLang already implements all of this. About 150 lines, no SGLang changes. |
…e, tuned fused-MoE config) and the table summarizer
What
SGLang cannot load SpecForge's MoE-FFN drafters, so the Qwen3.8-27B DSpark MoE drafters trained by #811 / #812 /
kan/moe-3-qwen38(e.g.RadixArk/qwen38-dspark-moe-3ep-cont-step9916) could only be served from a private SGLang copy, and untuned. This PR adds a serving package SGLang loads throughSGLANG_EXTERNAL_MODEL_PACKAGE, plus the tuned fused-MoE kernel configs for that drafter's expert shape.specforge/serving/sglang_models/dflash_moe.py:Qwen3MoeDSparkModel(andDFlashMoEDraftModel/DFlash2MoEDraftModel) = SGLang's ownQwen3DSparkModel/DFlashDraftModel/DFlash2DraftModelwith each layer's MLP replaced by SGLang's own MoE block (TopK+FusedMoE+ a sigmoid-gated shared expert, the same modules asQwen2MoeSparseMoeBlock). Checkpoints load through the modules' own weight loaders (FusedMoE.make_expert_params_mappingfor the per-expert tensors), so expert-width padding, runner-specific layouts (Triton / flashinfer TRT-LLM), quantised checkpoints and TP sharding are handled by SGLang. A strict check refuses partially matching exports. Router logits are computed in fp32 with the folded centering bias exactly as the trainer does, so a token selects the same experts as in training. Qwen (experts.{i}.gate_proj) and DeepSeek (experts.{i}.w1) checkpoint naming are both accepted.specforge/serving/sglang_models/moe_ffn.py: the same FFN in plain PyTorch (no SGLang import), used as the reference by the gate and the tests.patches/sglang/moe_configs/: tuned fused-MoE Triton configsE=512,N=512for H200 and GB300 (SGLang ships none, so this shape runs with the generic tile config), the stand-inQwen3MoeForCausalLMconfig and a wrapper for SGLang's tuner, README with usage and numbers.scripts/gates/check_dspark_moe_sglang_equivalence.py: served block vs plain reference at a small shape and at the real one (512 x 512 top-10, shared 2048): identical top-k, combine weights within 1e-6, bf16-level output drift, truncated export refused;--moe-runner-backend triton|flashinfer_trtllm. Compares the reference against the trainer'sMoELayertoo when that package is in the checkout (it is not onmainyet).tests/test_serving/: CPU tests for the reference recipe, preset defaults and name mapping.Supported recipe: the
qwen3_5_moepreset (softmax, renormalised top-k, optional router bias, one sigmoid-gated shared expert). DeepSeek-V4-style recipes (sqrtsoftplus, selection bias, groups, SwiGLU clamp) raise a clear error.Usage
The export's
config.jsonmust sayarchitectures: ["Qwen3MoeDSparkModel"]with the Qwen MoE keys (num_experts,num_experts_per_tok,moe_intermediate_size,shared_expert_intermediate_size,moe_router_bias), which SpecForge's normalizer on the MoE branches writes.Validation
Gate (
check_dspark_moe_sglang_equivalence.py), real shape, 7 / 56 / 448 tokens: PASS on GB300 with the Triton and the flashinfer TRT-LLM runner, and on H200 (Triton); routing identical to the reference, max |dy| 1.6e-2 to 3.1e-2 at mean |y| 0.5 (bf16 level).End-to-end, bf16 target
Qwen/Qwen3.8-27B, drafters bf16,specforge benchmarkgsm8k / mt-bench (100 / 200 prompts, 512 new tokens), DSPARK block 7, triton attention, mamba cache 160, max 32 running requests. accept length / tok/s (speedup vs no draft):GB300 (4 GPUs, one server per GPU):
(dense measured alone after the others; the MoE rows ran concurrently.)
Loader check, this package (SGLang's weight loaders) vs a stacked direct-copy loader on the same checkpoint, kernel and config, started together: GB300 gsm8k c=1/8/32 339 / 1367 / 3395 vs 330 / 1342 / 3381 tok/s, mt-bench 218 / 1039 / 2353 vs 215 / 1025 / 2336; H200 (1 GPU, mamba 48, c=1/8) gsm8k 243 / 1173 vs 243 / 1128, mt-bench 157 / 815 vs 157 / 841. Same speed within noise; the 39 GB export loads in 8 s (GB300) / 38 s (H200). Accept lengths identical.
Tuning the kernel config is worth 0.4-1.8% here (0.5-1.1% with an FP8 target, see the README): the drafter's MoE FFN is bandwidth-bound at this expert shape and is 9-21% of a speculative step.
Relation to other PRs
kan/moe-3-qwen38; no trainer change.torch._grouped_mminside a patched sglang 0.5.18; this uses stock SGLang >= 0.5.19 and itsFusedMoE).feat/sglang-fused-moe-configs-qwen38-dspark-moe.🤖 Generated with Claude Code