fix: zero-init fp32 UE8M0 activation scales in the gemm1_alpha masked path - #32386
Open
yueming-yuan wants to merge 1 commit into
Open
fix: zero-init fp32 UE8M0 activation scales in the gemm1_alpha masked path#32386yueming-yuan wants to merge 1 commit into
yueming-yuan wants to merge 1 commit into
Conversation
yueming-yuan
requested review from
BBuf,
Edwardf0t1,
Fridge003,
HaiShaw,
Ying1123,
ch-wan,
ispobock and
merrymercy
as code owners
July 25, 2026 06:58
Contributor
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
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.
Problem
deep_gemm requires every fp32 scale factor handed to its scale-layout transform to be a positive power of two (UE8M0). It enforces this with a device-side assert:
0x807fffffmasks the sign bit and the mantissa, so anything with a nonzero mantissa fails. Because it is a device assert, the CUDA context is poisoned and the user-visible error is the unhelpful719 (CUDA_ERROR_LAUNCH_FAILED, unspecified launch failure).The transform validates the entire padded scale tensor, including rows at or beyond
masked_m. Any masked kernel that allocates its fp32 scale buffer withtorch.emptyand only writes the active rows therefore leaves the inactive rows holding whatever was in that memory, which is almost never a power of two.Where this is reachable on
mainIn
_varlen_deep_gemm_silu_mul_quant, thegemm1_alphabranch:When
DEEPGEMM_SCALE_UE8M0is on but any of the other three conditions fails — most easilyG % 4 != 0— the buffer is fp32 and uninitialized, andsilu_and_mul_masked_post_quant_fwdwrites only rows belowmasked_m.The other two branches are not affected: the
swiglu_limit/swizzlebranch assertsG % 4 == 0, so with UE8M0 enabled it is always packed int32; and the default plain-silu path routes throughper_token_group_quant(..., column_major_scales=True), which lands on the packed-int32 allocation increate_per_token_group_quant_fp8_output_scaleand so never reaches the fp32 validation.Evidence
Minimal reproduction — the same masked grouped GEMM twice, differing only in the inactive rows:
Confirming that inactive rows are validated, and that valid-but-unwritten padding is the whole problem. Reproduced on both B200 (sm_100) and B300 (sm_103) with deep_gemm 0.1.4; the behaviour and the
DEEPGEMM_BLACKWELL/DEEPGEMM_SCALE_UE8M0/DEEPGEMM_NEED_TMA_ALIGNED_SCALESflags are identical on both, so this is not architecture-specific.Fix
Allocate the fp32 case with
torch.zeros.0.0is0x00000000, which satisfies the assert, and the value in inactive rows cannot affect the result becausemasked_mexcludes them from the GEMM. The packed int32 case keepstorch.emptysince it never reaches the fp32 validation.Rejected alternatives: bounding the transform by
masked_minside deep_gemm is arguably more correct — validating inactive rows is pointless work — but that is a change in a different project and needs per-expert row bounds. Emitting packed int32 from this branch is not possible without paddingGup to a multiple of 4, which is exactly the condition the packed path already requires.Verification, and what is not verified
The same class of bug, at the equivalent site on the
sglang-milesbranch (where the default plain-silu path still used the raw fp32torch.empty), was reproduced and fixed end to end — see the companion PR #32385. Qwen3-30B-A3B RL on 4xB300 with fp8 rollout went from 1125 asserts and a hard failure during decode CUDA graph capture to zero asserts and three complete training steps, withtrain_rollout_logprob_abs_diffstable at 0.033-0.035 across three separate 3-step runs (cpu offload, node-local disk offload, and disk offload plus optimizer-state streaming).I have not executed the
gemm1_alphapath onmain— I do not have a model configuration that exercises oai-swiglu withG % 4 != 0on hand. The change here is the same one-line allocation swap, justified by the minimal reproduction above and by reading the branch conditions; a reviewer who runs that path should sanity-check it.