Fix fused MLA YaRN RoPE query kernel corrupting upstream tensor in-place - #5412
Fix fused MLA YaRN RoPE query kernel corrupting upstream tensor in-place#5412ShauryaaSharma wants to merge 4 commits into
Conversation
The rotary_fwd_q_kernel Triton kernel writes the rotated query back to the input buffer in-place. When called from MLASelfAttention, the buffer passed as Q shares storage with the output of linear_q_up_proj. TransformerEngine saves that output for weight-gradient computation (dW = input^T @ grad_output); the in-place Triton write silently overwrites that saved value, producing incorrect dW on the backward pass and therefore corrupted weights after the optimizer step. The corruption manifests as NaN in the forward pass of the second training iteration. Fix by cloning q inside ApplyMLARotaryEmbQ.forward() before the kernel call. The clone has identical shape and strides and satisfies all existing assertions; no callers or interfaces change. The KV path (rotary_fwd_kv_kernel) already writes to freshly allocated o_key and o_value buffers and is unaffected. Add a regression test (TestFusedApplyMLARopeNoInputMutation) that verifies the input tensor is byte-for-byte unchanged after fused_apply_mla_rope_for_q returns, parameterized over both sbhd and thd input formats. Signed-off-by: ShauryaaSharma <shauryasofficial27@gmail.com>
|
This PR has been automatically converted to draft because all PRs must start as drafts. When you are ready for review, click Ready for Review to begin the review process. This will:
See the contribution guide for more details. |
|
/ok to test 59acdf2 |
|
Hey Team, following up on this PR, which has been ready for review for about a week. All CI checks are passing (70/70), |
|
/ok to test 5641ee3 |
|
Hey Team, following up on this PR, which has been ready for review for about two week. All CI checks are passing (70/70), /ok to test was approved by @Phlip79 and @guihong-nv , and the Final Review label is set. The only remaining step is a merge from code owners approval. Would appreciate it if someone could take a look when capacity allows. Thank you. |
|
/ok to test b1f04ac |
|
@ShauryaaSharma Thanks for the fix and the regression test. I took a closer look at the claimed TE aliasing path, and I think we need to validate the premise before making this out-of-place. In the in-tree MLA caller, This makes Could you add a minimal end-to-end reproducer that compares the Q projection wgrad with the fused and reference paths (including the relevant delayed-wgrad/TE configuration), or point to the specific consumer that retains the unrotated Q buffer? Otherwise, I think we should avoid the clone or retarget the fix. |
|
@guihong-nv Thanks for pushing on this, you're right, and I want to correct the record rather than defend the original claim. I pulled def wgrad_gemm(x, dy, ...):
"""Perform wgrad GEMM: dw = dy^T * x"""
dw, db, *_ = general_gemm(x, dy, **wgrad_gemm_kwargs)called as
Given that, I don't think the
Appreciate you catching this before it merged on a wrong justification. |
|
Thanks for validating this and correcting the record. I agree with the conclusion: I do not know of an in-tree consumer that retains the unrotated projection output, so I’d take option 1—drop the clone and its associated API/test changes, then close or narrow this PR. The cos/sin-width guard in #5497 is the appropriate follow-up for the reported NaN path. |
What does this PR do ?
Stop the fused MLA RoPE query kernel (
rotary_fwd_q_kernel) from modifying its input in-place, which silently corrupts the activation TransformerEngine saved for the weight gradient and produces incorrectdW.Problem
When
apply_rope_fusion=Truefor MLA (Multi-Latent Attention),rotary_fwd_q_kernelmodifies the query tensorQin-place. The buffer passed asQshares underlying storage with the output oflinear_q_up_proj, because the caller doesq = linear_q_up_proj(q_compressed)thenq = q.view(...)— a zero-copy reshape.TransformerEngine saves that linear-layer output for weight-gradient computation (
dW = input^T @ grad_output). The in-place Triton write silently overwrites those saved bytes before backward runs, producing incorrectdWand therefore corrupted model weights after the optimizer step.The result is a silent correctness bug — wrong gradients that degrade training rather than failing loudly. (The iteration-2 NaN reported in #5317 has a separate root cause: an out-of-bounds cos/sin read when
rotary_percent < 1shrinks the cache belowqk_pos_emb_head_dim. This PR therefore relates to #5317 rather than closing it.)Solution
Clone
qinsideApplyMLARotaryEmbQ.forward()before the Triton kernel call. The clone is contiguous, satisfiesstride(-1) == 1, and has identical shape and strides — no callers or public interfaces change.The KV path (
rotary_fwd_kv_kernel) already allocates fresho_keyando_valueoutput buffers and is not affected.Changes
megatron/core/fusions/fused_mla_yarn_rope_apply.py: addq = q.clone()before kernel dispatch inApplyMLARotaryEmbQ.forward(); update docstrings to reflect that the input tensor is no longer modified.tests/unit_tests/fusions/test_mla_yarn_rope_apply.py: addTestFusedApplyMLARopeNoInputMutation— verifies the input tensor is byte-for-byte unchanged afterfused_apply_mla_rope_for_qreturns, parameterized oversbhdandthdformats.Issue tracking
Linked issue: Related to #5317
Contribution process
Pre-checks