Thanks to visit codestin.com
Credit goes to github.com

Skip to content

add SP support for _flash_3_varlen_hub backend#13809

Merged
sayakpaul merged 15 commits into
huggingface:mainfrom
zhtmike:fa3
Jul 8, 2026
Merged

add SP support for _flash_3_varlen_hub backend#13809
sayakpaul merged 15 commits into
huggingface:mainfrom
zhtmike:fa3

Conversation

@zhtmike

@zhtmike zhtmike commented May 26, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

A follow up work for #13479. I have added _flash_3_varlen_hub support for SP forward & backward.

Tested with QwenImage pipeline, the result image is expected.
Tested with QwenImage training with SP, there is no error.
The UTs for Flux and QwenImage are passed.

Fixes # (issue)

Before submitting

Who can review?

@sayakpaul

Anyone in the community is free to review the PR once the tests have passed. Feel free to tag
members/contributors who may be interested in your PR.

@github-actions github-actions Bot added size/L PR with diff > 200 LOC models tests and removed size/L PR with diff > 200 LOC labels May 26, 2026
Comment on lines -3004 to -3017
if attn_mask is not None:
attn_mask = _normalize_attn_mask(attn_mask, batch_size, seq_len_kv)

(_, seqlens_k), (cu_seqlens_q, cu_seqlens_k), (max_seqlen_q, max_seqlen_k) = (
_prepare_for_flash_attn_or_sage_varlen(
batch_size, seq_len_q, seq_len_kv, attn_mask=attn_mask, device=query.device
)
)

key_valid, value_valid = [], []
for b in range(batch_size):
valid_len = seqlens_k[b]
key_valid.append(key[b, :valid_len])
value_valid.append(value[b, :valid_len])

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This seems like should come under if _parallel_config is None and attn_mask is not None:?

@zhtmike zhtmike Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

There is a little refactor in this PR, and basically it is doing the same thing as
#13479 (comment)

max_seqlen_k=max_seqlen_k,
softmax_scale=scale,
causal=is_causal,
return_attn_probs=return_lse,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This seems like an extra argument?

@zhtmike zhtmike Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yes, we following the other implementation's style.

And actually return_attn_probs by default is False (see `https://github.com/huggingface/kernels-community/blob/main/flash-attn3/torch-ext/flash_attn3/flash_attn_interface.py#L648)

So it will only return single tensor (see https://github.com/huggingface/kernels-community/blob/main/flash-attn3/torch-ext/flash_attn3/flash_attn_interface.py#L691),

the previous code should not be runnable.

return_attn_probs=return_lse,
)
if return_lse:
out, lse, *_ = out

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why do we need to initialize lse = None above?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It is a guard similar to _native_cudnn_attention, _sage_attention, etc. To make the code in same style

@sayakpaul

Copy link
Copy Markdown
Member

@askserge could you do a review?

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🤗 Serge says:

This PR adds sequence parallel (context parallel) support for the _flash_3_varlen_hub attention backend, following the same pattern established by the existing _flash_varlen_hub (flash-attn2) and _flash_3_hub implementations. The code is well-structured and closely mirrors the existing patterns.

Correctness

  • Potential bug: indices_k used before assignment on the no-mask path. In _flash_attention_3_varlen_hub_forward_op, when attn_mask is None, the variable indices_k is never assigned, but at line 1721 ctx.indices_k = indices_k if attn_mask is not None else None — this is actually fine because the conditional guards it. However, if _save_ctx is False, indices_k is never referenced at all on the no-mask path, so there's no issue. This matches the flash-attn2 varlen pattern exactly.

  • Positional argument fragility in wrapped_forward_fn call. The non-varlen _flash_attention_3_hub_forward_op uses keyword arguments for causal, window_size_left, etc., but the new varlen forward op passes everything positionally (lines 1677–1712). This makes the code harder to read and more fragile if the upstream _flash_attn_forward signature changes. Consider using keyword arguments for at least the trailing parameters, consistent with the non-varlen version.

  • return_lse handling change in the non-SP path. The original code always unpacked out, lse, *_ from the function call. The new code passes return_attn_probs=return_lse and conditionally unpacks. This is a behavioral change for the non-SP path — if return_lse=False, the function now returns a single tensor instead of a tuple. This should be verified to work correctly with the flash_attn_varlen_func API. The flash-attn2 varlen hub uses return_attn_probs=return_lse similarly, so this is likely correct.

Tests

  • The new backend is properly added to ContextParallelAttentionBackendsTesterMixin and the ring_degree skip logic.
  • The _FLASH_3_VARLEN_HUB is added to the hub kernels set in tests/models/testing_utils/utils.py.

Minor Issues

  • Bug in existing test code (pre-existing). Line 413 (deleted): attention_backend in ("flash_varlen_hub") — using in with a parenthesized string (not a tuple) means this is just attention_backend in "flash_varlen_hub", which checks character membership, not string membership. The fix on line 417 adds a proper tuple ("flash_varlen_hub", "_flash_3_varlen_hub"), which is correct. However, the original single-element check was buggy. Good that it's fixed now.

Overall the implementation follows established patterns well and looks correct.

17 LLM turns · 18 tool calls · 92.5s · 309778 in / 4124 out tokens

Comment thread src/diffusers/models/attention_dispatch.py
Comment thread src/diffusers/models/attention_dispatch.py Outdated
max_seqlen_k=max_seqlen_k,
softmax_scale=scale,
causal=is_causal,
return_attn_probs=return_lse,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Note: The original code always unpacked out, lse, *_ = func(...). Now with return_attn_probs=return_lse, when return_lse=False the return value may be different (single tensor vs tuple). Make sure flash_attn_varlen_func from flash-attn3 returns a single tensor (not a tuple) when return_attn_probs=False. The flash-attn2 varlen hub uses the same pattern, so this is likely fine, but worth verifying.

@zhtmike zhtmike Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@github-actions github-actions Bot added the size/L PR with diff > 200 LOC label Jun 3, 2026
@zhtmike

zhtmike commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

Hi @sayakpaul sorry for late update. The review should be addressed, can you take a look?

@sayakpaul

Copy link
Copy Markdown
Member

Waiting for PR #13756.

@zhtmike

zhtmike commented Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

Waiting for PR #13756.

Per the discussion in PR #13756, I have added a precision test in ContextParallelAttentionBackendsTesterMixin.

I have tested with:

  • tests/models/transformers/test_models_transformer_qwenimage.py::TestQwenImageTransformerContextParallelAttnBackends
  • tests/models/transformers/test_models_transformer_flux.py::TestFluxTransformerContextParallelAttnBackends

and the tests passed.

Meanwhile, there is a breaking change in huggingface/kernels-community@1471f94 that causes FA2 to fail to run with recent pre‑built kernels (missing _wrapped_flash_attn_forward), so I have also fixed that in the PR. Please take a look!

@zhtmike

zhtmike commented Jun 29, 2026

Copy link
Copy Markdown
Contributor Author

@sayakpaul please take a look if you have time~

@sayakpaul

Copy link
Copy Markdown
Member

Could you please resolve the conflicts?

@sergereview sergereview Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🤗 Serge says:

Clean, well-scoped port of the existing FLASH_VARLEN_HUB context-parallel implementation to the _FLASH_3_VARLEN_HUB backend. The new forward/backward ops mirror the established FA3 (_flash_attention_3_hub_*) and FA2-varlen (_flash_varlen_attention_hub_*) patterns closely, and the wiring all cross-checks:

  • The FA3 _flash_attn_forward/_flash_attn_backward positional call signatures match those already used by _flash_attention_3_hub_forward_op/_backward_op, with the varlen-specific cu_seqlens/max_seqlen args inserted in the right positions.
  • ctx.indices_k = indices_k if attn_mask is not None else None is safe — the ternary short-circuits before indices_k (only defined in the masked branch) is evaluated.
  • The non-CP path change to gate lse via return_attn_probs=return_lse and unpack conditionally is correct and avoids requesting lse unnecessarily.
  • ring_degree > 1 is explicitly rejected in both the dispatch function and the test skip list; ulysses is the supported path.
  • functools is imported; the registry wrapped_*_attr entries point at the FA3 flash_attn_interface._flash_attn_{forward,backward} which matches the long-signature calls.

Tests

  • The _context_parallel_worker already accepts the new state_dict positional arg, so the added argument at the call site is consistent. Capturing weights from the (bf16-cast) reference model and reloading them into a fresh model in the worker before its own cast ensures both sides use identical weights — this upgrades the test from a shape-only check to a real numerical assert_close, which is a genuine improvement.

No correctness or security concerns found. The description's claims (SP forward/backward support, Flux/QwenImage tests) are consistent with the diff.

serge v0.1.0 · model: claude-opus-4-8 · 14 LLM turns · 14 tool calls · 89.5s · 439489 in / 5510 out tokens

# Conflicts:
#	src/diffusers/models/attention_dispatch.py
@zhtmike

zhtmike commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

Could you please resolve the conflicts?

Done!

Comment on lines +350 to +351
wrapped_forward_attr="flash_attn_interface._flash_attn_forward",
wrapped_backward_attr="flash_attn_interface._flash_attn_backward",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can you explain these changes?

@zhtmike zhtmike Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

there is a breaking change in huggingface/kernels-community@1471f94 in PR huggingface/kernels-community#877 that causes FA2 to fail to run with recent pre‑built kernels (missing _wrapped_flash_attn_forward), so I have to fix it here to pass the tests.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Cc: @drbh. Does this need a version bump?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Just run with the latest kernels version; it seems to be fixed. I’ve reverted the changes.

wrapped_forward_attr="flash_attn_interface._wrapped_flash_attn_varlen_forward",
wrapped_backward_attr="flash_attn_interface._wrapped_flash_attn_varlen_backward",
wrapped_forward_attr="flash_attn_interface._flash_attn_varlen_forward",
wrapped_backward_attr="flash_attn_interface._flash_attn_varlen_backward",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

as above.

AttentionBackendName._FLASH_3_VARLEN_HUB,
constraints=[_check_device, _check_qkv_dtype_bf16_or_fp16, _check_shape],
supports_context_parallel=False,
supports_context_parallel=True,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🤌

batch_size, seq_len_q, _, _ = query.shape
_, seq_len_kv, _, _ = key.shape

if attn_mask is not None:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Sorry I have lost the context here since it's been a while. Can you explain why do we need these changes? I don't think the if _parallel_config is None: branch is doing what we had previously.

@zhtmike zhtmike Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

For if _parallel_config is None: branch, I major do a fix for non-contiguous mark handling for models like Qwen-Image, following the pattern of https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention_dispatch.py#L2783.

Both implementations are equivalent for contiguous masks, but the previous implementation was buggy for non‑contiguous masks.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can that come in a separate PR before we merge this PR? I think the two are slightly unrelated?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

OK, sure~

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Please check #14115

@zhtmike

zhtmike commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

Updated the PR with latest main, tested with tests/models/transformers/test_models_transformer_qwenimage.py::TestQwenImageTransformerContextParallelAttnBackends and tests/models/transformers/test_models_transformer_flux.py::TestFluxTransformerContextParallelAttnBackends, they are passing

@sayakpaul

Copy link
Copy Markdown
Member

tests/models/transformers/test_models_transformer_qwenimage.py::TestQwenImageTransformerContextParallelAttnBackends

For this I think only varlen applies since Qwen has masks?

@zhtmike

zhtmike commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

tests/models/transformers/test_models_transformer_qwenimage.py::TestQwenImageTransformerContextParallelAttnBackends

For this I think only varlen applies since Qwen has masks?

yeah right!

@HuggingFaceDocBuilderDev

Copy link
Copy Markdown

The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update.

@sayakpaul sayakpaul left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Ran pytest tests/models/transformers/ -k "test_context_parallel_attn_backend_inference" on 2 H100s and all were green! Let's go.

@sayakpaul sayakpaul merged commit 59d4e5a into huggingface:main Jul 8, 2026
13 of 15 checks passed
@zhtmike zhtmike deleted the fa3 branch July 9, 2026 02:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

models size/L PR with diff > 200 LOC tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants