Skip to content

[megatron] Fix FA4 backward recompilation: pass max_seqlen as int in PackedSeqParams - #9798

Merged
tastelikefeet merged 1 commit into
modelscope:mainfrom
xuwang0117:fix/megatron-packed-seq-max-seqlen-int
Jul 30, 2026
Merged

[megatron] Fix FA4 backward recompilation: pass max_seqlen as int in PackedSeqParams#9798
tastelikefeet merged 1 commit into
modelscope:mainfrom
xuwang0117:fix/megatron-packed-seq-max-seqlen-int

Conversation

@xuwang0117

Copy link
Copy Markdown
Contributor

Fixes #9797

PR type

  • Bug Fix

PR information

get_packed_seq_params() passed max_seqlen_q/kv as 0-dim CUDA tensors (from cu_seqlens.diff().max(), no .item()).

flash-attn 4 (CuTe DSL) embeds booleans derived from max_seqlen into its backward-kernel compile-cache key. A 0-dim tensor there hashes by object identity, so the cache never hits and every micro-batch backward triggers a full JIT recompilation (tens of seconds each). With expert parallelism the other ranks stall on MoE all-to-all while any rank compiles, so the whole step is serialized on compilation. The forward key does not contain these fields, so only backward is affected.

This PR converts max_seqlen_q/kv to Python int before constructing PackedSeqParams (one microsecond-level device sync per micro-batch, negligible). This also matches the flash-attn varlen API contract (max_seqlen is documented/typed as int; transformers/modeling_flash_attention_utils.py follows the same convention).

Root-cause evidence (from hooking flash_attn.cute.interface._flash_attn_bwd.compile_cache during real training): 16 compilations of the same key within the first minute, with the key ending in

(..., tensor(False, device='cuda:0'), tensor(False, device='cuda:0'))

Experiment results

8×B200, Qwen3.5-35B-A3B full-parameter SFT (megatron sft, packing + padding_free, EP=8, max_length=8192, --attention_backend auto):

setup per-step GPU util memory
no flash-attn (unfused fallback) 39.2 s/it 145.6 GiB
FA4 4.0.0b11, before fix 482–535 s/it ~0% (CPU-bound compiling) 81 GiB
FA4 4.0.0b11, after fix 13–14 s/it 50–70% 87.5 GiB

Loss curve identical before/after (first-step loss 1.3514 in both).

🤖 Generated with Claude Code

…PackedSeqParams

get_packed_seq_params() passed max_seqlen_q/kv as 0-dim CUDA tensors
(from cu_seqlens.diff().max()). flash-attn 4 (CuTe DSL) embeds booleans
derived from max_seqlen into its backward-kernel compile-cache key, and
a tensor there hashes by object identity, so the cache never hits and
every micro-batch backward triggers a full JIT recompilation.

Measured on 8xB200 (Qwen3.5-35B-A3B SFT, packing + padding_free, EP=8):
- before: 482-535 s/it (GPUs ~0% util, all ranks stall on MoE all-to-all
  while one rank compiles)
- after: 13-14 s/it steady state

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@tastelikefeet
tastelikefeet merged commit 5afcce5 into modelscope:main Jul 30, 2026
3 of 5 checks passed
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.

[megatron] flash-attn 4 + packing: backward kernel recompiles every micro-batch (>10x slowdown) because max_seqlen is a 0-dim CUDA tensor

2 participants