[ROCm] Fix THD/ragged dQ backward on the bf16 dq_shuffle path (gfx950)#657
[ROCm] Fix THD/ragged dQ backward on the bf16 dq_shuffle path (gfx950)#657wenchenvincent wants to merge 1 commit into
Conversation
The CK backward's dq_acc scratch was always allocated fp32-packed (nsplits, H, total_q, d_qk) with batch_stride_dq_acc=0 in group mode. That layout only matches the fp32 dq_convert post-kernel (NVTE_CK_IS_V3_ATOMIC_FP32=1). On the default bf16 dq_shuffle path (atomic_fp32=0, gfx950) the post-kernel expects a per-segment padded layout, so with batch_stride=0 every ragged segment past cu_seqlens offset 0 is written to the offset-0 slot -- dQ is correct only for the first segment and garbage for the rest, silently corrupting gradients for packed/THD training (forward, dK, dV are unaffected). Mirror aiter's asm_mha_varlen_bwd wiring: in ragged/group mode with atomic_fp32=0, allocate dq_acc as bf16 (nsplits, B, H, pad16(s_q), 128) and set stride_dq_acc=128, nhead_stride=pad16(s_q)*128, batch_stride=H*pad16(s_q)*128, split_stride=B*H*pad16(s_q)*128, so each segment lands in its own fixed-stride slot. The fp32 dq_convert path and non-ragged BSHD/SBHD are unchanged. Verified on MI355X (gfx950): te thd padding_causal backward dQ cos 0.0036 -> 1.0000 with atomic_fp32=0, all segments correct across [256], [100,80,76], [128,128], [64x4], [200,56]; still uses the fast bwd_hd128_dq_shuffle_group kernel; atomic_fp32=1 unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| // (nsplits, B, H, pad16(s_q), 128) so each ragged segment has a fixed-stride slot (batch_stride!=0). | ||
| // TE previously always used the fp32-packed layout with batch_stride_dq_acc=0, which the dq_shuffle | ||
| // kernel mis-indexes for segments past cu_seqlens offset 0 -> corrupt dQ. See ck_fused_attn_bwd.cpp. | ||
| const bool dq_acc_bf16_ragged = is_ragged && !nvte_ck_is_v3_atomic_fp32; |
There was a problem hiding this comment.
For safety it should probably also check nvte_ck_uses_bwd_v3
There was a problem hiding this comment.
Emm, even if checking nvte_ck_uses_bwd_v3, I still have one concern: we don't have a way to know whether we fall back to v2 path. Currently v3 asm path coverage is still limited. Once we fall back to v2, there is no bf16 atomic path... @wenchenvincent
There was a problem hiding this comment.
So we may need adding extra call with v3_api_check
|
Superseded by #671, which rebases this fix onto |
Summary
On ROCm/gfx950 the CK fused-attention backward silently corrupts dQ for THD/ragged (packed / document-masked) attention when using the default bf16 accumulation path (
NVTE_CK_IS_V3_ATOMIC_FP32=0). Forward, dK and dV are correct.Root cause
fused_attn_ck_bwd_implalways allocates thedq_accscratch as fp32-packed(nsplits, H, total_q, d_qk)andck_fused_attn_bwdsetsbatch_stride_dq_acc = 0in group mode. That layout only matches the fp32dq_convertpost-kernel (atomic_fp32=1). On the bf16dq_shufflepost-kernel (atomic_fp32=0), the kernel expects a per-segment padded layout — withbatch_stride=0every ragged segment pastcu_seqlensoffset 0 is written to the offset-0 slot, so dQ is correct only for the first segment and garbage for the rest.Fix
Mirror aiter's
asm_mha_varlen_bwdwiring. In ragged/group mode withatomic_fp32=0, allocatedq_accas bf16(nsplits, B, H, pad16(s_q), 128)and setstride_dq_acc=128,nhead_stride=pad16(s_q)*128,batch_stride=H*pad16(s_q)*128,split_stride=B*H*pad16(s_q)*128. The fp32dq_convertpath and non-ragged BSHD/SBHD are unchanged.Verification (MI355X / gfx950)
TE THD
padding_causalbackward vs a pure-JAX block-diagonal reference,NVTE_CK_IS_V3_ATOMIC_FP32=0:+1.0000across[256],[100,80,76],[128,128],[64×4],[200,56].bwd_hd128_dq_shuffle_groupkernel (fix is on the fast path, not a fallback).atomic_fp32=1path unchanged (no regression).Note:
fused_attn_ck_hip.cppis generated by hipify fromfused_attn_ck.cpp, so only the source is changed here.🤖 Generated with Claude Code