Skip to content

[ROCm] Fix THD/ragged dQ backward on the bf16 dq_shuffle path (gfx950)#657

Open
wenchenvincent wants to merge 1 commit into
devfrom
fix/thd-dq-atomic16-dq-acc-layout
Open

[ROCm] Fix THD/ragged dQ backward on the bf16 dq_shuffle path (gfx950)#657
wenchenvincent wants to merge 1 commit into
devfrom
fix/thd-dq-atomic16-dq-acc-layout

Conversation

@wenchenvincent

Copy link
Copy Markdown
Collaborator

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_impl always allocates the dq_acc scratch as fp32-packed (nsplits, H, total_q, d_qk) and ck_fused_attn_bwd sets batch_stride_dq_acc = 0 in group mode. That layout only matches the fp32 dq_convert post-kernel (atomic_fp32=1). On the bf16 dq_shuffle post-kernel (atomic_fp32=0), the kernel expects a per-segment padded layout — with batch_stride=0 every ragged segment past cu_seqlens offset 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_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. The fp32 dq_convert path and non-ragged BSHD/SBHD are unchanged.

Verification (MI355X / gfx950)

TE THD padding_causal backward vs a pure-JAX block-diagonal reference, NVTE_CK_IS_V3_ATOMIC_FP32=0:

  • dQ cosine 0.0036 → 1.0000; per-segment all +1.0000 across [256], [100,80,76], [128,128], [64×4], [200,56].
  • Still uses the fast bwd_hd128_dq_shuffle_group kernel (fix is on the fast path, not a fallback).
  • atomic_fp32=1 path unchanged (no regression).
  • End-to-end: MaxText llama3.1-8B block-diagonal THD packing now trains correctly (previously stalled ~6.1 loss and NaN'd).

Note: fused_attn_ck_hip.cpp is generated by hipify from fused_attn_ck.cpp, so only the source is changed here.

🤖 Generated with Claude Code

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;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For safety it should probably also check nvte_ck_uses_bwd_v3

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we may need adding extra call with v3_api_check

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants