fix(specialized_clm): contiguous q/k/v for flash backward under torch.compile on ROCm#116
fix(specialized_clm): contiguous q/k/v for flash backward under torch.compile on ROCm#116thomas-schweich wants to merge 1 commit into
Conversation
…torch.compile on ROCm SpecializedCLM._Block fed non-contiguous q/k/v (transpose + RoPE, no .contiguous()) into F.scaled_dot_product_attention. Under torch.compile on ROCm the inductor flash-attention *backward* kernel then hit a stride mismatch: AssertionError: ... torch.ops.aten._scaled_dot_product_flash_attention_backward expected size 4==4, stride 16384==64 at dim=1 ... so the dedicated CLM could only run with --no-compile or sdpa_math. The main PAWN model's Attention.forward already forces q/k/v contiguous for exactly this reason (pawn/model.py); apply the same to the CLM block. Lets specialized_clm train with flash + torch.compile + bf16 on ROCm. Found while benchmarking v1 cotrain vs the v2 supernet.
| v = self.wv(h).view(B, T, self.n_heads, self.head_dim).transpose(1, 2).contiguous() | ||
| # Force contiguous q/k/v before SDPA so flash attention's backward | ||
| # gets the strides it expects under torch.compile on ROCm — same | ||
| # fix as the main model's Attention.forward (pawn/model.py). Without | ||
| # it the inductor flash-backward kernel hits a stride mismatch. | ||
| q = _apply_rope(q, rope_cos, rope_sin).contiguous() | ||
| k = _apply_rope(k, rope_cos, rope_sin).contiguous() |
There was a problem hiding this comment.
Minor: the comment says "Force contiguous q/k/v" but v is already on the line before it, so the explanation arrives after the first call it covers. Moving the comment block above all three makes the intent clearer and matches how you'd normally read it:
| v = self.wv(h).view(B, T, self.n_heads, self.head_dim).transpose(1, 2).contiguous() | |
| # Force contiguous q/k/v before SDPA so flash attention's backward | |
| # gets the strides it expects under torch.compile on ROCm — same | |
| # fix as the main model's Attention.forward (pawn/model.py). Without | |
| # it the inductor flash-backward kernel hits a stride mismatch. | |
| q = _apply_rope(q, rope_cos, rope_sin).contiguous() | |
| k = _apply_rope(k, rope_cos, rope_sin).contiguous() | |
| # Force contiguous q/k/v before SDPA so flash attention's backward | |
| # gets the strides it expects under torch.compile on ROCm — same | |
| # fix as the main model's Attention.forward (pawn/model.py). Without | |
| # it the inductor flash-backward kernel hits a stride mismatch. | |
| v = self.wv(h).view(B, T, self.n_heads, self.head_dim).transpose(1, 2).contiguous() | |
| q = _apply_rope(q, rope_cos, rope_sin).contiguous() | |
| k = _apply_rope(k, rope_cos, rope_sin).contiguous() |
| # it the inductor flash-backward kernel hits a stride mismatch. | ||
| q = _apply_rope(q, rope_cos, rope_sin).contiguous() | ||
| k = _apply_rope(k, rope_cos, rope_sin).contiguous() | ||
| attn_out = F.scaled_dot_product_attention(q, k, v, is_causal=True) |
There was a problem hiding this comment.
Pre-existing gap (not introduced by this PR, but visible here): model.py's Attention.forward wraps this call with sdpa_kernel(SDPA_BACKEND) when SDPA_BACKEND is not None, so --sdpa-math takes effect there. specialized_clm never checks SDPA_BACKEND and always calls SDPA bare, so --sdpa-math is silently ignored for the specialized_clm strategy. Not a blocker for this fix, but worth a follow-up if --sdpa-math ever needs to work here as a debugging escape hatch.
ReviewFix is correct and minimal. The three Findings
One nit and one pre-existing gap to track; nothing blocking. |
Summary
SpecializedCLM._Blockpassed non-contiguous q/k/v (transpose + RoPE, no.contiguous()) intoF.scaled_dot_product_attention. Undertorch.compileon ROCm the inductor flash-attention backward kernel then asserts a stride mismatch:so the dedicated CLM could only train with
--no-compileorsdpa_math(MATH backend) on ROCm — i.e. it couldn't use the flash + compile + bf16 path the main model already uses.The main PAWN model's
Attention.forwardalready forces q/k/v contiguous for exactly this reason (pawn/model.py). This applies the same 3-line fix to the CLM block.Change
pawn/specialized_clm.py:.contiguous()onv(after transpose) and onq/k(after RoPE), mirroringmodel.py.Effect
specialized_clmnow trains with flash + torch.compile + bf16 on ROCm (verified locally on a 7900 XT: clean single-bucket compile, no flash-backward assert).Provenance
Found while benchmarking v1
cotrainvs the v2 supernet — the CLM was the one path that still fell back off flash on ROCm. Targetingmainso it lands pre-v2.Test plan
specialized_clmtrains to completion with default (flash + compile + bf16) on ROCm, no stride assert.