replace enable_gpa with repeat_interleave for krea2 - #14523
Merged
Conversation
Krea 2 always attends with a text padding mask, and no fused SDPA kernel takes a mask together with mismatched query/key head counts — flash rejects the mask, the memory-efficient kernel rejects the mismatch. Attention therefore fell back to the math backend, which materializes the full [batch_size, num_heads, seq_len, seq_len] score matrix with no error or warning. Repeating the key/value heads in the processor computes the same thing and keeps the memory-efficient kernel eligible: at 1024x1024 (48/12 heads, 4608 tokens) one attention call goes from 9.02 GiB / 26.7 ms to 0.16 GiB / 4.1 ms. It also unpins the model from the native backend, since cuDNN, flash, FA3, sage and the hub kernels all raise on `enable_gqa`. Fixes #14518 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
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
approved these changes
Aug 18, 2026
sayakpaul
left a comment
Member
There was a problem hiding this comment.
Thanks! Just flagging the following table:
Claude derived it by using the following code and running it against this branch and main:
from diffusers import Krea2Transformer2DModel
device, dtype = "cuda", torch.bfloat16
model = Krea2Transformer2DModel.from_pretrained(
"krea/Krea-2-Raw", subfolder="transformer", torch_dtype=dtype
).to(device)
model.eval()
# 1024x1024 image -> 128x128 latents -> 64x64 grid of patch-2 tokens;
# 512 text positions with only the first 77 valid (padding mask exercised).
torch.manual_seed(0)
batch, text_seq_len, grid = 1, 512, 64
image_seq_len = grid * grid
hidden_states = torch.randn(batch, image_seq_len, 64, device=device, dtype=dtype)
encoder_hidden_states = torch.randn(batch, text_seq_len, 12, 2560, device=device, dtype=dtype)
timestep = torch.tensor([0.7], device=device)
encoder_attention_mask = torch.zeros(batch, text_seq_len, dtype=torch.bool, device=device)
encoder_attention_mask[:, :77] = True
text_ids = torch.zeros(text_seq_len, 3, device=device)
image_ids = torch.zeros(grid, grid, 3, device=device)
image_ids[..., 1] = torch.arange(grid, device=device)[:, None]
image_ids[..., 2] = torch.arange(grid, device=device)[None, :]
position_ids = torch.cat([text_ids, image_ids.reshape(-1, 3)], dim=0)
with torch.no_grad():
out = model(
hidden_states=hidden_states,
encoder_hidden_states=encoder_hidden_states,
timestep=timestep,
position_ids=position_ids,
encoder_attention_mask=encoder_attention_mask,
).sampleI think this is acceptable because now the kernel invocations internally have changed.
|
|
||
| ### Grouped-query attention | ||
|
|
||
| Fewer key/value heads than query heads can be spelled two ways. Either pass `enable_gqa=True` to `dispatch_attention_fn` and let the backend broadcast, or repeat the key/value heads in the processor after RoPE and pass no flag (`transformer_krea2.py`, `transformer_nucleusmoe_image.py`): |
Member
There was a problem hiding this comment.
Either pass
enable_gqa=Truetodispatch_attention_fn
Can we also supplement a model file here for the reference?
|
|
||
| - **Compatibility.** Most backends do not implement `enable_gqa` yet — flash, FA3, sage, cuDNN and the hub kernels raise on it, as does the context-parallel path. Grep `enable_gqa` in `attention_dispatch.py` for the current list rather than trusting this one; it changes as support lands. The flag limits the model to whichever backends still accept it, while repeating works on all of them. | ||
|
|
||
| - **Performance.** Turns on whether the model passes a mask. With a mask, no fused kernel takes a mask *and* mismatched head counts, so SDPA falls back to math and materializes the full `[batch_size, num_heads, seq_len_q, seq_len_kv]` score matrix — no error, no warning, only memory. Without a mask, flash broadcasts inside the kernel and the flag saves the key/value copy. Both effects scale with sequence length and head count, so measure at the model's real shape; `torch.backends.cuda.can_use_flash_attention(params, debug=True)` and `can_use_efficient_attention` print why a kernel was rejected, which is the fastest way to see which one you actually got. |
Member
There was a problem hiding this comment.
Turns on whether the model passes a mask.
Do we mean performance is turned on?
asomoza
approved these changes
Aug 18, 2026
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


fix #14518