Skip to content

Commit 5be68ce

Browse files
zhtmikesayakpaul
andauthored
fix _flash_3_varlen_hub mask handling (#14115)
* fix fa3 mask * add test case --------- Co-authored-by: Sayak Paul <spsayakpaul@gmail.com>
1 parent cbdb637 commit 5be68ce

4 files changed

Lines changed: 67 additions & 14 deletions

File tree

src/diffusers/models/attention_dispatch.py

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3024,22 +3024,20 @@ def _flash_attention_3_varlen_hub(
30243024

30253025
if attn_mask is not None:
30263026
attn_mask = _normalize_attn_mask(attn_mask, batch_size, seq_len_kv)
3027-
3028-
(_, seqlens_k), (cu_seqlens_q, cu_seqlens_k), (max_seqlen_q, max_seqlen_k) = (
3029-
_prepare_for_flash_attn_or_sage_varlen(
3030-
batch_size, seq_len_q, seq_len_kv, attn_mask=attn_mask, device=query.device
3027+
(_, _), (cu_seqlens_q, cu_seqlens_k), (max_seqlen_q, max_seqlen_k) = (
3028+
_prepare_for_flash_attn_or_sage_varlen_with_mask(batch_size, seq_len_q, attn_mask, query.device)
30313029
)
3032-
)
3033-
3034-
key_valid, value_valid = [], []
3035-
for b in range(batch_size):
3036-
valid_len = seqlens_k[b]
3037-
key_valid.append(key[b, :valid_len])
3038-
value_valid.append(value[b, :valid_len])
3030+
indices_k = attn_mask.flatten().nonzero(as_tuple=False).flatten()
3031+
key_packed = key.reshape(-1, *key.shape[2:])[indices_k]
3032+
value_packed = value.reshape(-1, *value.shape[2:])[indices_k]
3033+
else:
3034+
(_, _), (cu_seqlens_q, cu_seqlens_k), (max_seqlen_q, max_seqlen_k) = (
3035+
_prepare_for_flash_attn_or_sage_varlen_without_mask(batch_size, seq_len_q, seq_len_kv, query.device)
3036+
)
3037+
key_packed = key.flatten(0, 1)
3038+
value_packed = value.flatten(0, 1)
30393039

30403040
query_packed = query.flatten(0, 1)
3041-
key_packed = torch.cat(key_valid, dim=0)
3042-
value_packed = torch.cat(value_valid, dim=0)
30433041

30443042
func = _HUB_KERNELS_REGISTRY[AttentionBackendName._FLASH_3_VARLEN_HUB].kernel_fn
30453043
result = func(

tests/models/testing_utils/attention.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,38 @@
7070
],
7171
)
7272

73+
_PARAM_FLASH_VARLEN_HUB = pytest.param(
74+
AttentionBackendName.FLASH_VARLEN_HUB,
75+
id="flash_varlen_hub",
76+
marks=[
77+
pytest.mark.skipif(not _CUDA_AVAILABLE, reason="CUDA is required for flash_varlen_hub backend."),
78+
pytest.mark.skipif(
79+
not is_kernels_available(),
80+
reason="`kernels` package is required for flash_varlen_hub backend. Install with `pip install kernels`.",
81+
),
82+
],
83+
)
84+
85+
_PARAM_FLASH_3_VARLEN_HUB = pytest.param(
86+
AttentionBackendName._FLASH_3_VARLEN_HUB,
87+
id="flash_3_varlen_hub",
88+
marks=[
89+
pytest.mark.skipif(not _CUDA_AVAILABLE, reason="CUDA is required for _flash_3_varlen_hub backend."),
90+
pytest.mark.skipif(
91+
not is_kernels_available(),
92+
reason="`kernels` package is required for _flash_3_varlen_hub backend. Install with `pip install kernels`.",
93+
),
94+
],
95+
)
96+
7397
# All backends under test.
74-
_ALL_BACKEND_PARAMS = [_PARAM_NATIVE_CUDNN, _PARAM_FLASH_HUB, _PARAM_FLASH_3_HUB]
98+
_ALL_BACKEND_PARAMS = [
99+
_PARAM_NATIVE_CUDNN,
100+
_PARAM_FLASH_HUB,
101+
_PARAM_FLASH_3_HUB,
102+
_PARAM_FLASH_VARLEN_HUB,
103+
_PARAM_FLASH_3_VARLEN_HUB,
104+
]
75105

76106
# Backends that perform non-deterministic operations and therefore cannot run when
77107
# torch.use_deterministic_algorithms(True) is active (e.g. after enable_full_determinism()).
@@ -267,6 +297,8 @@ class AttentionBackendTesterMixin:
267297
Use `pytest -m "not attention"` to skip these tests.
268298
"""
269299

300+
unsupported_attn_backends: list[str] = []
301+
270302
def setup_method(self):
271303
gc.collect()
272304
backend_empty_cache(torch_device)
@@ -280,6 +312,8 @@ def teardown_method(self):
280312
def test_set_attention_backend_matches_context_manager(self, backend):
281313
"""set_attention_backend() and the attention_backend() context manager must yield identical outputs."""
282314
_skip_if_backend_requires_nondeterminism(backend)
315+
if backend.value in self.unsupported_attn_backends:
316+
pytest.skip(f"{backend.value} is not supported for this model.")
283317

284318
init_dict = self.get_init_dict()
285319
inputs_dict = self.get_dummy_inputs()
@@ -318,6 +352,8 @@ def test_set_attention_backend_matches_context_manager(self, backend):
318352
def test_output_close_to_native(self, backend, atol=1e-2, rtol=1e-2):
319353
"""All backends should produce model output numerically close to the native SDPA reference."""
320354
_skip_if_backend_requires_nondeterminism(backend)
355+
if backend.value in self.unsupported_attn_backends:
356+
pytest.skip(f"{backend.value} is not supported for this model.")
321357

322358
init_dict = self.get_init_dict()
323359
inputs_dict = self.get_dummy_inputs()
@@ -378,6 +414,8 @@ def test_compile(self, backend, atol=1e-2, rtol=1e-2):
378414
as opposed to `model.compile`).
379415
"""
380416
_skip_if_backend_requires_nondeterminism(backend)
417+
if backend.value in self.unsupported_attn_backends:
418+
pytest.skip(f"{backend.value} is not supported for this model.")
381419
if getattr(self.model_class, "_repeated_blocks", None) is None:
382420
pytest.skip("Skipping tests as regional compilation is not supported.")
383421

tests/models/testing_utils/utils.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
AttentionBackendName.FLASH_HUB,
99
AttentionBackendName.FLASH_VARLEN_HUB,
1010
AttentionBackendName._FLASH_3_HUB,
11+
AttentionBackendName._FLASH_3_VARLEN_HUB,
1112
}
1213

1314

tests/models/transformers/test_models_transformer_qwenimage.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
from ...testing_utils import enable_full_determinism, torch_device
2424
from ..testing_utils import (
25+
AttentionBackendTesterMixin,
2526
AttentionTesterMixin,
2627
BaseModelTesterConfig,
2728
BitsAndBytesTesterMixin,
@@ -250,6 +251,21 @@ class TestQwenImageTransformerAttention(QwenImageTransformerTesterConfig, Attent
250251
"""Attention processor tests for QwenImage Transformer."""
251252

252253

254+
class TestQwenImageTransformerAttentionBackend(QwenImageTransformerTesterConfig, AttentionBackendTesterMixin):
255+
"""Attention backend tests for QwenImage Transformer."""
256+
257+
unsupported_attn_backends = ["flash_hub", "_flash_3_hub"]
258+
259+
def get_dummy_inputs(self, batch_size: int = 2):
260+
inputs = super().get_dummy_inputs(batch_size=batch_size)
261+
mask = inputs["encoder_hidden_states_mask"]
262+
mask[...] = 0
263+
mask[0, :2] = 1
264+
mask[1, :6] = 1
265+
inputs["encoder_hidden_states_mask"] = mask.bool()
266+
return inputs
267+
268+
253269
class TestQwenImageTransformerContextParallel(QwenImageTransformerTesterConfig, ContextParallelTesterMixin):
254270
"""Context Parallel inference tests for QwenImage Transformer."""
255271

0 commit comments

Comments
 (0)