Repository navigation
Conversation
The one-block-per-(token, group) kernel issues one 2-byte load per thread and a shared-memory tree reduction, reaching ~0.9 TB/s at prefill sizes, so it was 1.3-2.2x slower than the unfused SiLU+Mul and group-quant kernels it replaces (e.g. 293 vs 133 us for 65536x1536 on H200). Add a variant that handles each group with group_size/8 lanes of a warp: 16B loads of gate and up, a warp-shuffle max reduction, and 8B stores. It is used for 16-bit inputs with aligned rows and at least 3072 groups; smaller launches keep the existing kernel, which has lower latency there. Both produce bitwise-identical results. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: mgoin <mgoin64@gmail.com>
With VLLM_USE_DEEP_GEMM_E8M0=1 (the default when DeepGEMM is available, including Hopper), every block-FP8 MoE activation quant rounded its scales up to powers of two, even when the selected kernel (Triton, FlashInfer, CUTLASS) takes float32 scales. That only adds quantization error. Record the decision per layer in FusedMoEQuantConfig.use_ue8m0_act_scales, set by make_fp8_moe_quant_config from the selected backend (or a scale_fmt=ue8m0 checkpoint), and thread it through the no-DP/naive-DP prepare steps and the Triton experts. The sorted-A TMA down-projection path follows the same per-layer setting: its SiLU+Mul+quant kernel gains a float32-scale mode matching per_token_group_quant_fp8. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: mgoin <mgoin64@gmail.com>
TritonExperts skipped silu_and_mul_per_block_quant whenever DeepGEMM E8M0 was enabled (the default on Hopper with DeepGEMM), with a SwiGLU clamp, or with a non-[128, 128] block shape, silently falling back to two kernels. - silu_and_mul_per_block_quant gains use_ue8m0 (power-of-two scales, matching per_token_group_quant_fp8) and clamp_limit options. - TritonExperts uses the fused kernel for any SiLU FP8 block config with group size 64/128, with the layer's activation-scale format and the configured clamp; remaining fallbacks (non-SiLU, LoRA, other group sizes) emit warning_once with the reason. - BatchedTritonExperts uses persistent_masked_m_silu_mul_quant for SiLU FP8 block-128, with the same fallback warning. - The sorted-A TMA path's Triton SiLU+Mul+quant kernel now uses the same arithmetic as the CUDA kernel (float32 SiLU*up, no intermediate BF16 rounding), keeping both layouts bitwise identical. It no longer needs global DeepGEMM E8M0, since the route-major path is now always fused. - The SiluMul+BlockQuant compile fusion now forwards the E8M0 flag instead of silently dropping UE8M0 rounding. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: mgoin <mgoin64@gmail.com>
mgoin
requested review from
AndreasKaratzas,
BoyuanFeng,
ProExpertProg,
WoosukKwon,
bnellnm,
pavanimajety,
tlrmchlsmth,
wzhao18,
yewentao256,
youkaichao,
zou3519 and
zyongye
as code owners
November 5, 2026 03:05
6 tasks done
Member
Author
|
Confirmation run on this exact branch (
|
Contributor
|
This pull request has merge conflicts that must be resolved before it can be |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
On Hopper with DeepGEMM installed,
is_deep_gemm_e8m0_used()is true by default for every layer. This forces two costs on the Triton FP8 MoE path, which takes float32 scales:silu_and_mul_per_block_quant, so SiLU+Mul and quant run as two kernels.This PR makes the scale format a per-layer property of the selected MoE backend, and runs the fused kernel in every case it can serve.
It supersedes #64906 (same goal for the activation scales) and #43399 (UE8M0 option for the fused kernel). It also covers interactions those PRs predate: the sorted-A TMA path from #64760, the batched Triton experts, and the compile fusion.
Depends on #65791: the first commit here is #65791, which fixes the fused kernel's prefill performance. Without it, enabling the fused path by default costs -3.8% prefill. Only the last two commits are new.
Claims
4527515cd7.scale_fmt: ue8m0.Validation
Qwen/Qwen3-30B-A3B-FP8, H200 TP1,
--moe-backend auto(Triton), defaultVLLM_USE_DEEP_GEMM_E8M0=1.vllm bench serve --dataset-name random --ignore-eos: prefill 4096/1 at concurrency 64, mixed 1024/512 at concurrency 128, decode 1024/256.34d5de18cdwith these changes; the code is identical after the rebase, apart from [GLM 5.3 Perf] Sorted-A TMA for FP8 MoE down projection on H200, 6.0%~ 7.6% E2E Throughput Improvement #64760 interplay, which doesn't affect this model.gsm8k is 5-shot over 1319 questions, with stderr ~0.009. With
--moe-backend deep_gemmthe score is 0.8939 / 0.8582. On TP2 + EP (FlashInfer CUTLASS, which quantizes internally, so the compute is unchanged) it is 0.8893 / 0.8575 vs 0.8946 / 0.8628.GLM-5.3 TP8 shape (H=6144, I/TP=256, E=256, top-8), on main
4527515cd7, Triton layer time, CUDA graph, p50, two runs within 0.3%:Dense
Qwen/Qwen3-8B-FP8: perf and gsm8k are unchanged (59.7k vs 59.6k prefill tok/s; 0.8840 / 0.8772 both). On Hopper its FP8 linear quantizes inside the FlashInfer DeepGEMM op, so the compile fusion does not match.Tests (H200, E8M0 on and off; every failure listed also fails on main). Confirmed on this exact branch (see the comment below for the run):
tests/kernels/moe/test_block_fp8.py:deep_gemm_fused_moe"WorkspaceManager not initialized" errors. On main the same setting fails those 128 errors plus 18 tolerance cases andtest_sorted_a_tma_moe_matches_route_major.test_sorted_a_tma_moe_matches_route_major(bitwise, now parametrized over UE8M0 and fp32 scales) passes with E8M0 on and off.tests/kernels/core/test_fused_silu_mul_block_quant.py: 371 passed. The new cases cover the UE8M0 and clamp options with opcheck, and vectorized-vs-scalar bitwise checks.tests/kernels/moe/test_deepgemm.pypasses, including the newtest_e8m0_act_scales_only_for_deepgemm_backend.test_batched_moe.py -k fused_moe_batched_experts: 288 passed, with E8M0 on and off.tests/compile/passes/test_silu_mul_quant{,_manual}_fusion.py: 126 passed.Details
FusedMoEQuantConfig.use_ue8m0_act_scalesis set bymake_fp8_moe_quant_configfrom the selected backend: True for DeepGEMM backends or ascale_fmt=ue8m0checkpoint, False otherwise, None (global behavior) when there is no vLLM config. It is read by the no-DP and naive-DP prepare steps,moe_kernel_quantize_input, the Triton experts' quant calls, the fused kernel and the sorted-A kernel.silu_and_mul_per_block_quantgainsuse_ue8m0andclamp_limit, so the E8M0 and SwiGLU-clamp cases no longer fall back. Remaining fallbacks logwarning_oncewith the reason: non-SiLU, LoRA (it needs the bf16 activation) and group sizes other than 64/128.max(amax/448, 1/(448*512)), thenx/scale. It also takes the layer's scale format, so the two layouts stay bitwise identical and the path no longer requires global E8M0.BatchedTritonExpertsusespersistent_masked_m_silu_mul_quantfor SiLU block-128 instead of activation followed by a per-expert quant loop with.item()syncs. It still falls back with a SwiGLU clamp, since that kernel has no clamp.SiluMul+BlockQuantfusion now forwards the UE8M0 flag; it previously replaced a UE8M0 quant pattern with an fp32-scale fused op. This overlaps [Bugfix] Stop fusing scale_ue8m0=True group quant into ue8m0-unaware fused kernels #51477.test_block_fp8's fp32-SiLU reference now applies on CUDA too (it was ROCm-only), since the fused path now runs there by default.Pull Request Checklist
I used vLLM's
/pr-checklistskill. (Mandatory for agents, optional for humans).AI assistance was used during the creation of this PR.
Design Fit: Minimizes impact on core components, reuses existing functionality, and justifies added complexity.
Testing and Validation: Validates the change and ensures any added tests are meaningful and reliable, with CI coverage or documented CI resource constraints and validation performed outside CI.
Code Quality and Style: Keeps code and comments clear and concise, and updates relevant documentation and examples.
Pull Request Contents: Includes a brief summary and relevant links, supports claims with evidence, explains root causes and implementation trade-offs, and follows the contributing guide.