Skip to content

[quantization] NVFP4 MoE: split fused w13 gate/up global scales - #27588

Merged
merrymercy merged 5 commits into
sgl-project:mainfrom
prajjwal1:nvfp4-moe-split-w13-global-scales
Jun 15, 2026
Merged

merrymercy merged 5 commits into
sgl-project:mainfrom
prajjwal1:nvfp4-moe-split-w13-global-scales

Conversation

@prajjwal1

@prajjwal1 prajjwal1 commented Jun 8, 2026 •

Copy link
Copy Markdown
Contributor

Motivation

In an NVFP4 MoE layer the w13 GEMM fuses the gate (w1) and up (w3)
projections. Some checkpoints quantize the two halves with separate NVFP4 weight
global scales, stored as [num_experts, 2] (column 0 for gate, column 1 for up).

Today the fused-MoE path drops column 1: it keeps only the gate scale and logs a
w1_weight_scale_2 must match w3_weight_scale_2 warning when the columns differ.
The up half of the GEMM1 output then gets dequantized with the gate scale, which
is wrong whenever the two halves were quantized separately.

Modifications

  • modelopt_quant.py: add _compute_gemm1_alphas, which folds the GEMM1 input
    scale into the per-half weight scales and returns both g1_alphas (gate) and
    g1_alphas_up (up). Column 1 is read only when it exists, so 1-D,
    single-column, and non-gated checkpoints still collapse to one shared scale.
    Both alphas are registered on the layer; single-alpha backends such as CUTLASS
    keep reading g1_alphas.
  • flashinfer_trtllm.py: add _compute_g1_scale_c. The FlashInfer TRT-LLM
    kernel dequantizes the two GEMM1 halves separately, so g1_scale_c now uses
    the up alpha. With a shared scale it reduces to the previous single-scale
    value.
  • The Marlin path still uses a single shared scale; that collapse just moved
    into the Marlin branch, which is now its only consumer.
  • Add CPU unit tests for the split and for g1_scale_c.

Checkpoints with a shared (or near-equal) w1/w3 scale behave exactly as before.
Only checkpoints with genuinely different gate/up scales change, and they now
dequantize each half correctly.

Accuracy Tests

test/registered/unit/layers/quantization/test_modelopt_nvfp4_moe_scales.py
(CPU, no GPU) pins the contract of both helpers: the gate/up split and that the
two columns stay decoupled, a simulated GEMM1 checking each half reconstructs
from its own alpha, the shared-scale fallbacks (1-D / single-column / equal
columns), and the non-gated case. All pass locally.

The TRT-LLM MoE path only runs on Blackwell, and most checkpoints ship
near-equal gate/up scales, so this regression wouldn't surface in a normal
accuracy eval. The unit tests cover it directly instead.

Speed Tests and Profiling

No effect on the forward path. The change runs once in
process_weights_after_loading and only adds one small per-expert tensor
(g1_alphas_up) at load time.

Checklist

  • Format code with pre-commit (black / isort / ruff F401,F821).
  • Add unit tests.
  • Update documentation (not applicable).
  • Provide accuracy and speed benchmark results (CPU unit tests cover the numerics; see above).
  • Follow the SGLang code style guidance.

Original commits

  • ac341a49e

CI States

Latest PR Test (Base): ✅ Run #27511566795
Latest PR Test (Extra): ❌ Run #27511566758

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@prajjwal1

Copy link
Copy Markdown
Contributor Author

/tag-and-rerun-ci

@github-actions github-actions Bot added quant LLM Quantization blackwell SM100/SM120 labels Jun 8, 2026
@prajjwal1
prajjwal1 force-pushed the nvfp4-moe-split-w13-global-scales branch from ec68e52 to be819e5 Compare June 8, 2026 18:10
The fused w13 GEMM in an NVFP4 MoE layer packs the gate (w1) and up (w3)
projections together. A checkpoint may carry a distinct NVFP4 weight global
scale per half, stored as [num_experts, 2] (col 0 = gate, col 1 = up).
Previously the fused-MoE path collapsed these to the gate scale (col 0) and
warned when the two columns differed, so the up half of GEMM1's output was
dequantized with the wrong scale.

Compute both g1_alphas (gate) and g1_alphas_up (up), and let the FlashInfer
TRT-LLM kernel dequantize each half of GEMM1's output with its own scale via
g1_scale_c. Shared-scale checkpoints (1-D or single-column) collapse back to
the original single-scale path, and non-gated (Relu2) layers are unchanged.
The Marlin path continues to use a single shared scale.

Add CPU unit tests pinning the gate/up scale split (_compute_gemm1_alphas)
and the derived TRT-LLM up-half output scalar (_compute_g1_scale_c).
@prajjwal1
prajjwal1 force-pushed the nvfp4-moe-split-w13-global-scales branch from be819e5 to ba6bac9 Compare June 8, 2026 18:13
@merrymercy

Copy link
Copy Markdown
Collaborator

/tag-and-rerun-ci

@github-actions github-actions Bot added the run-ci CI: run the baseline test suite on this PR label Jun 9, 2026
@prajjwal1

Copy link
Copy Markdown
Contributor Author

@merrymercy can you please tell me what are the next steps for landing this ?

prajjwal1 added 2 commits June 12, 2026 12:38
…it tests

Rewrite test_modelopt_nvfp4_moe_scales.py to parametrize the gate/up
global-scale split and g1_scale_c contracts over representative NVFP4
ModelOpt MoE shapes (DeepSeek-R1, Llama-4, Qwen3, Mixtral, and a non-gated
Relu2 config) with realistic small fp32 global-scale magnitudes, instead of
toy 2-3 expert sizes.

Fix the g1_scale_c tests to use the real TRT-LLM scalar w2_input_scale_quant
(1 / w2_input_scale.max()) rather than a per-expert tensor, and assert the
per-expert output shape plus the non-gated .expand().contiguous() contract;
keep a per-expert broadcast case for robustness.

Document that the surrounding wiring (Marlin single-scale collapse, the
w1/w3 mismatch warning, and the layer-level g1_alphas / g1_alphas_up
registration in process_weights_after_loading) is Blackwell-only and is
exercised on-device, not by these CPU tests.
Add a test for the per-expert w13_input_scale shape the non-TRT-LLM backends
pass (the existing tests only exercised the scalar TRT-LLM shape), drop the
over-specific model-dim claim from the module docstring, and tighten the
comments.
@prajjwal1

Copy link
Copy Markdown
Contributor Author

/rerun-test test_modelopt_nvfp4_moe_scales.py test_qwen3_fp4_trtllm_gen_moe.py::TestFlashinferTrtllmGenMoeBackend test_nvidia_nemotron_3_super_nvfp4.py::TestNvidiaNemotron3SuperNVFP4 test_deepseek_v3_fp4.py::TestDeepseekV3FP4SymmetricMemory

@github-actions

Copy link
Copy Markdown
Contributor

⛔ /rerun-test is not available for fork PRs unless the commenter has write permission on the repo.

Please ask a maintainer to run this command, or use the normal CI flow.

@prajjwal1

Copy link
Copy Markdown
Contributor Author

/tag-and-rerun-ci

@prajjwal1

Copy link
Copy Markdown
Contributor Author

/rerun-test test_modelopt_nvfp4_moe_scales.py test_qwen3_fp4_trtllm_gen_moe.py::TestFlashinferTrtllmGenMoeBackend test_nvidia_nemotron_3_super_nvfp4.py::TestNvidiaNemotron3SuperNVFP4 test_deepseek_v3_fp4.py::TestDeepseekV3FP4SymmetricMemory

@github-actions

Copy link
Copy Markdown
Contributor

⛔ /rerun-test is not available for fork PRs unless the commenter has write permission on the repo.

Please ask a maintainer to run this command, or use the normal CI flow.

@prajjwal1

Copy link
Copy Markdown
Contributor Author

/tag-and-rerun-ci extra

@github-actions github-actions Bot added the run-ci-extra CI: also run the extra suite (requires run-ci) label Jun 13, 2026
@merrymercy

Copy link
Copy Markdown
Collaborator

/rerun-test test_modelopt_nvfp4_moe_scales.py test_qwen3_fp4_trtllm_gen_moe.py::TestFlashinferTrtllmGenMoeBackend test_nvidia_nemotron_3_super_nvfp4.py::TestNvidiaNemotron3SuperNVFP4 test_deepseek_v3_fp4.py::TestDeepseekV3FP4SymmetricMemory

@github-actions

github-actions Bot commented Jun 14, 2026 •

Copy link
Copy Markdown
Contributor

Results for /rerun-test test_modelopt_nvfp4_moe_scales.py test_qwen3_fp4_trtllm_gen_moe.py::TestFlashinferTrtllmGenMoeBackend test_nvidia_nemotron_3_super_nvfp4.py::TestNvidiaNemotron3SuperNVFP4 test_deepseek_v3_fp4.py::TestDeepseekV3FP4SymmetricMemory:

🚀 ubuntu-latest (1 test): ✅ View workflow run

cd test/ && python3 registered/unit/layers/quantization/test_modelopt_nvfp4_moe_scales.py

🚀 1-gpu-h100 (1 test): ✅ View workflow run

cd test/ && python3 registered/backends/test_qwen3_fp4_trtllm_gen_moe.py TestFlashinferTrtllmGenMoeBackend

🚀 4-gpu-b200 (2 tests): ❌ View workflow run

cd test/ && python3 registered/4-gpu-models/test_nvidia_nemotron_3_super_nvfp4.py TestNvidiaNemotron3SuperNVFP4
cd test/ && python3 registered/models_e2e/test_deepseek_v3_fp4.py TestDeepseekV3FP4SymmetricMemory

@merrymercy

Copy link
Copy Markdown
Collaborator

/rerun-test test_modelopt_nvfp4_moe_scales.py test_qwen3_fp4_trtllm_gen_moe.py::TestFlashinferTrtllmGenMoeBackend test_nvidia_nemotron_3_super_nvfp4.py::TestNvidiaNemotron3SuperNVFP4 test_deepseek_v3_fp4.py::TestDeepseekV3FP4SymmetricMemory

@github-actions

github-actions Bot commented Jun 14, 2026 •

Copy link
Copy Markdown
Contributor

Results for /rerun-test test_modelopt_nvfp4_moe_scales.py test_qwen3_fp4_trtllm_gen_moe.py::TestFlashinferTrtllmGenMoeBackend test_nvidia_nemotron_3_super_nvfp4.py::TestNvidiaNemotron3SuperNVFP4 test_deepseek_v3_fp4.py::TestDeepseekV3FP4SymmetricMemory:

🚀 ubuntu-latest (1 test): ✅ View workflow run

cd test/ && python3 registered/unit/layers/quantization/test_modelopt_nvfp4_moe_scales.py

🚀 1-gpu-h100 (1 test): ✅ View workflow run

cd test/ && python3 registered/backends/test_qwen3_fp4_trtllm_gen_moe.py TestFlashinferTrtllmGenMoeBackend

🚀 4-gpu-b200 (2 tests): ✅ View workflow run

cd test/ && python3 registered/4-gpu-models/test_nvidia_nemotron_3_super_nvfp4.py TestNvidiaNemotron3SuperNVFP4
cd test/ && python3 registered/models_e2e/test_deepseek_v3_fp4.py TestDeepseekV3FP4SymmetricMemory

@merrymercy merrymercy removed the run-ci-extra CI: also run the extra suite (requires run-ci) label Jun 14, 2026
@merrymercy
merrymercy merged commit 441b75e into sgl-project:main Jun 15, 2026
190 of 218 checks passed
Chronostasys pushed a commit to MindLab-Research/sglang that referenced this pull request Aug 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

blackwell SM100/SM120 quant LLM Quantization run-ci CI: run the baseline test suite on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants