Fix: fused QUproj + RoPE + Quant - #3421
Conversation
FusedMLAQUpProjFunction now owns the normalization instead of consuming a pre-normalized BF16 activation. TELayerNormColumnParallelLinear hands its input quantizer to the norm kernel, so MXFP8 is produced in one step from the FP32 accumulator; splitting that into a standalone norm plus a separate quantize rounds twice, which under NVTE_NORM_FWD_USE_CUDNN=1 moves ~3.3% of the E4M3 codes and perturbs the rsigma the norm backward consumes. FusedMLAQUpProjRopeQuant.run therefore takes an already-quantized input on the FP8 path rather than quantizing again, and backward gained the RMSNorm bwd. Scope is RMSNorm at TP=1; both are asserted. Replaces the env-gated FusedMLAQUpProjNormFunction that carried this on cont-fused: there is now one path, not two.
Updated comments in FusedMLAQUpProjRopeQuant and FusedMLAQUpProjFunction. Streamlined the description of the fused Q up-projection process. Signed-off-by: Ravi Ghadia <rghadia@nvidia.com>
Greptile SummaryThis PR moves RMSNorm inside the fused MLA Q up-projection autograd function so its FP32 accumulator can be quantized directly to MXFP8 before fused GEMM, RoPE, and output quantization.
Confidence Score: 4/5The PR should not merge until the RoPE backward launch validates that the rotary interval fits within each query head; the missing API documentation is non-blocking. Caller-controlled head metadata can make the new Triton backward kernel cross head boundaries and access beyond the gradient allocation because its mask bounds only the starting offset. Files Needing Attention: transformer_engine/pytorch/attention/fused_mla_q_uproj.py and transformer_engine/pytorch/init.py Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart LR
X[Pre-normalization input] --> N[RMSNorm]
N --> Q[Direct MXFP8 quantization]
Q --> G[Fused Q up-projection GEMM]
G --> R[Per-head RoPE]
R --> O[MXFP8 query]
O --> RB[Inverse RoPE backward]
RB --> LB[Linear backward]
LB --> NB[RMSNorm backward]
Reviews (1): Last reviewed commit: "refactor: simplify comments and document..." | Re-trigger Greptile |
| rotary_bwd_q_kernel[grid]( | ||
| dq3, | ||
| cos.contiguous(), | ||
| sin.contiguous(), | ||
| qk_head_dim, | ||
| qk_pos_emb_head_dim, | ||
| nh, | ||
| 1, | ||
| None, | ||
| None, | ||
| dq3.stride(0), | ||
| dq3.stride(1), | ||
| 0, | ||
| 1, | ||
| ) |
There was a problem hiding this comment.
Unbounded per-head RoPE access
When q_head_dim matches the fused output width but qk_head_dim + qk_pos_emb_head_dim exceeds it, the backward kernel's mask bounds only each head's starting offset, so its loads and stores cross into adjacent heads and beyond the final head, causing corrupted gradients or a CUDA illegal-memory-access failure.
Knowledge Base Used: PyTorch attention execution
| from transformer_engine.pytorch.module import UserBufferQuantizationMode | ||
| from transformer_engine.pytorch.attention import DotProductAttention | ||
| from transformer_engine.pytorch.attention import FusedMLAQUpProjRopeQuant | ||
| from transformer_engine.pytorch.attention import FusedMLAQUpProjFunction, FusedMLAQUpProjRopeQuant |
There was a problem hiding this comment.
Undocumented public autograd API
FusedMLAQUpProjFunction is now exported from the root and attention namespaces without a corresponding entry in docs/api/pytorch.rst, leaving its extensive argument and shape contract absent from the generated public API reference.
Knowledge Base Used: PyTorch runtime and public API
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Description
Modify the FusedMLAQUpProjFunction to absorb norm inside the autgrad function, so that the fp32 output from the normalization is directly quantized to MXFP8 without intermediate bf16 rounding.
Fixes # (issue)
Type of change
Changes
Please list the changes introduced in this PR:
Checklist: