Repository navigation
[model, ci] fix: scatter Flux joint attention output per stream under Ulysses SP #1248
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Coach257
merged 5 commits into
ByteDance-Seed:main
from
OnePunchMonk:fix/flux-joint-attn-scatter
Oct 12, 2026
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
19cc696
[model, ci] fix: scatter Flux joint attention output per stream under…
OnePunchMonk 7317ff9
test: skip Flux Ulysses test on CPU
OnePunchMonk 599a101
test, ci: force SDPA for joint attn test and run it in GPU CI
OnePunchMonk 0ee1e9e
fix: use set_float32_matmul_precision for consistent tf32 handling in…
OnePunchMonk b89bd10
Merge origin/main into fix/flux-joint-attn-scatter
OnePunchMonk File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| """Multi-process regression test for Flux joint attention under Ulysses SP. | ||
|
|
||
| `FluxJointAttention` all-to-alls Q/K/V of the image and text streams, attends over the | ||
| concatenated `[text_full, image_full]` sequence, then used one reverse all-to-all on that | ||
| concatenation. That hands each rank a contiguous slice of `[text_full, image_full]`, which the | ||
| code then split at the local text length -- so every rank got a mix of text and image tokens | ||
| from the wrong positions, even with divisible lengths and no mask. | ||
|
|
||
| This drives `FluxJointAttention.forward` directly on local shards with `ulysses_size=world_size` | ||
| and checks the gathered image and text outputs against a full-sequence run of the same module. | ||
| """ | ||
|
|
||
| from unittest.mock import patch | ||
|
|
||
| import pytest | ||
| import torch | ||
| import torch.distributed as c10d | ||
|
|
||
| from veomni.utils.device import get_device_type, get_dist_comm_backend, get_torch_device | ||
|
|
||
|
|
||
| if get_device_type() == "cpu" or not c10d.is_available() or not c10d.is_backend_available(get_dist_comm_backend()): | ||
| pytest.skip("c10d NCCL not available, skipping tests", allow_module_level=True) | ||
|
|
||
| from torch.testing._internal.common_utils import run_tests | ||
|
|
||
| from veomni.distributed.parallel_state import _init_parallel_state, clear_parallel_state | ||
| from veomni.models.transformers.flux import modeling_flux | ||
| from veomni.models.transformers.flux.modeling_flux import FluxJointAttention | ||
|
|
||
| from .utils import SequenceParallelTest | ||
|
|
||
|
|
||
| def _rotary_embedding(seq_len, head_dim, device): | ||
| pos = torch.arange(seq_len, dtype=torch.float32) | ||
| ang = torch.outer(pos, torch.arange(head_dim // 2, dtype=torch.float32) * 0.1) | ||
| cos, sin = torch.cos(ang), torch.sin(ang) | ||
| return torch.stack([cos, -sin, sin, cos], dim=-1).reshape(1, 1, seq_len, head_dim // 2, 2, 2).to(device) | ||
|
|
||
|
|
||
| def _gather(x, group, world_size): | ||
| chunks = [torch.empty_like(x) for _ in range(world_size)] | ||
| c10d.all_gather(chunks, x.contiguous(), group=group) | ||
| return torch.cat(chunks, dim=1) | ||
|
|
||
|
|
||
| class FluxJointAttentionUlyssesTest(SequenceParallelTest): | ||
| @property | ||
| def world_size(self): | ||
|
OnePunchMonk marked this conversation as resolved.
Resolved
|
||
| return 2 | ||
|
|
||
| # `flash_attention` takes the flash_attn_interface/flash_attn path whenever either is | ||
| # importable and no mask is given, which is the no_mask case here. Those kernels only | ||
| # accept FP16/BF16, so this FP32 comparison has to run on SDPA. | ||
| @patch.object(modeling_flux, "FLASH_ATTN_3_AVAILABLE", False) | ||
| @patch.object(modeling_flux, "FLASH_ATTN_2_AVAILABLE", False) | ||
| @pytest.mark.skipif(get_torch_device().device_count() < 2, reason="device_count should be >= 2") | ||
| def test_matches_full_sequence_reference(self): | ||
| group = self._get_process_group() | ||
| device = torch.device(get_device_type(), self.rank) | ||
| torch.set_float32_matmul_precision("highest") | ||
| dim, num_heads, head_dim, txt_len, img_len = 64, 4, 16, 4, 16 | ||
| assert txt_len % self.world_size == 0 and img_len % self.world_size == 0 # isolate from padding | ||
|
|
||
| torch.manual_seed(0) | ||
| attn = FluxJointAttention(dim, dim, num_heads, head_dim).to(device) | ||
|
OnePunchMonk marked this conversation as resolved.
Resolved
|
||
| text = torch.randn(1, txt_len, dim, device=device) | ||
| image = torch.randn(1, img_len, dim, device=device) | ||
| rotary = _rotary_embedding(txt_len + img_len, head_dim, device) | ||
| # Entity-style mask: text token 0 may not attend to the second half of the image. | ||
| mask = torch.zeros(1, 1, txt_len + img_len, txt_len + img_len, device=device) | ||
| mask[..., 0, txt_len + img_len // 2 :] = float("-inf") | ||
| mask[..., txt_len + img_len // 2 :, 0] = float("-inf") | ||
|
|
||
| try: | ||
| _init_parallel_state(dp_size=self.world_size, ulysses_size=1, device_type=get_device_type(), name=None) | ||
| with torch.no_grad(): | ||
| refs = {name: attn(image, text, rotary, m) for name, m in [("no_mask", None), ("mask", mask)]} | ||
| clear_parallel_state() | ||
|
|
||
| _init_parallel_state(dp_size=1, ulysses_size=self.world_size, device_type=get_device_type(), name=None) | ||
| t_chunk, i_chunk = txt_len // self.world_size, img_len // self.world_size | ||
| text_local = text[:, self.rank * t_chunk : (self.rank + 1) * t_chunk] | ||
| image_local = image[:, self.rank * i_chunk : (self.rank + 1) * i_chunk] | ||
| for name, m in [("no_mask", None), ("mask", mask)]: | ||
| with torch.no_grad(): | ||
| out_image, out_text = attn(image_local, text_local, rotary, m) | ||
| ref_image, ref_text = refs[name] | ||
| torch.testing.assert_close( | ||
| _gather(out_image, group, self.world_size), ref_image, atol=1e-5, rtol=1e-4, msg=f"{name}: image" | ||
| ) | ||
| torch.testing.assert_close( | ||
| _gather(out_text, group, self.world_size), ref_text, atol=1e-5, rtol=1e-4, msg=f"{name}: text" | ||
| ) | ||
| finally: | ||
| clear_parallel_state() | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| run_tests() | ||
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
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.