Uh oh!
There was an error while loading. Please reload this page.
Fix Flux Context Parallel Bug (Incoherent Image Generation) - #12443
Fix Flux Context Parallel Bug (Incoherent Image Generation)#12443mali-afridi wants to merge 1 commit into
Conversation
Thanks for the PR. From the looks of it, it does seem like it is fully LLM-generated. Also, FWIW, we strive to keep our modeling implementations simple so, I am not sure yet if the changes align with that philosophy. @DN6 WDYT? |
DN6
commented
Oct 7, 2025
Hi @mali-afridi the issue seems to be because an unsupported backend is being used with CP. This snippet should work importtorchfromdiffusersimportFluxPipelinefromdiffusersimportContextParallelConfigtry:
torch.distributed.init_process_group("nccl")
rank=torch.distributed.get_rank()
device=torch.device("cuda", rank%torch.cuda.device_count())
torch.cuda.set_device(device)
device=torch.device("cuda")
pipe=FluxPipeline.from_pretrained("black-forest-labs/FLUX.1-dev", torch_dtype=torch.bfloat16)
pipe.to(device)
pipe.transformer.set_attention_backend("_native_cudnn")
pipe.transformer.enable_parallelism(config=ContextParallelConfig(ring_degree=2))
prompt="A picture of a cat holding a sign that says hello"# Must specify generator so all ranks start with same latents (or pass your own)generator=torch.Generator().manual_seed(42)
image=pipe(prompt, num_inference_steps=28, guidance_scale=4.0, generator=generator).images[0]
ifrank==0:
image.save("output.png")
exceptExceptionase:
raiseefinally:
iftorch.distributed.is_initialized():
torch.distributed.destroy_process_group()I've opened a PR to raise an error when an incompatible backend is used: #12446 |
mali-afridi
commented
Oct 7, 2025
Interesting, yeah the |
mali-afridi
commented
Oct 7, 2025
For Qwen Reproducibility: |
DefTruth
commented
Oct 17, 2025
I also encountered AssertionError: Tensor size along dimension to be sharded must be divisible by mesh size when testing the parallelism of qwen-image-edit. How can I fix this? I look forward to your solution. importtorchfromPILimportImagefromloguruimportloggerfromtransformersimportQwen2_5_VLForConditionalGeneration, Qwen2Tokenizer, Qwen2VLProcessorfromdiffusersimportQwenImagePipeline, ContextParallelConfig, QwenImageEditPlusPipeline, \
AutoencoderKLQwenImage, QwenImageTransformer2DModelfromoptimum.quantoimportfreeze, qfloat8_e4m3fn, quantizetorch.distributed.init_process_group("nccl")
rank=torch.distributed.get_rank()
device=torch.device("cuda", rank%torch.cuda.device_count())
torch.cuda.set_device(device)
model_id="/share/gdli7/models/checkpoints/Qwen/Qwen-Image-Edit-2509"dtype=torch.bfloat16quant_model=Truetransformer=QwenImageTransformer2DModel.from_pretrained(model_id, subfolder="transformer", torch_dtype=dtype)
text_encoder=Qwen2_5_VLForConditionalGeneration.from_pretrained(model_id, subfolder="text_encoder",
torch_dtype=dtype)
ifquant_model:
quantize(text_encoder, weights=qfloat8_e4m3fn)
freeze(text_encoder)
quantize(transformer, weights=qfloat8_e4m3fn)
freeze(transformer)
pipe=QwenImageEditPlusPipeline.from_pretrained(model_id,
# vae=vae,# image_encoder=image_encoder,transformer=transformer,
text_encoder=text_encoder,
# tokenizer=tokenizer,torch_dtype=dtype)
print(f"Current scheduler: {type(pipe.scheduler).__name__}")
print(f"Scheduler config: {pipe.scheduler.config}")
pipe.to("cuda")
pipe.vae.enable_tiling()
pipe.vae.enable_slicing()
pipe.transformer.enable_parallelism(config=ContextParallelConfig(ring_degree=2))
pipe.transformer.set_attention_backend("_native_cudnn")
image=Image.open("/share/gdli7/common/AIPhoto/yarn-art-pikachu.png").resize((512,512)).convert("RGB")
prompt="Make Pikachu hold a sign that says 'Qwen is awesome', yarn art style, detailed, vibrant colors"generator=torch.Generator().manual_seed(42)
image=pipe(image, prompt, num_inference_steps=50, generator=generator).images[0]
ifrank==0:
image.save("output.png")
iftorch.distributed.is_initialized():
torch.distributed.destroy_process_group() |
sayakpaul
commented
Oct 27, 2025
sayakpaul
commented
Oct 27, 2025
@mali-afridi what's your recommended fix for Qwen? |
mali-afridi
commented
Oct 27, 2025
@sayakpaul I would suggest padding to make the dimension divisible, like I did in chengzeyi/ParaAttention#53 |
sayakpaul
commented
Oct 27, 2025
Hmm, I see. That would be my preliminary approach, too. Can't we use masks to avoid that? |
DefTruth
commented
Oct 31, 2025
sayakpaul
commented
Nov 1, 2025
@DefTruth LMK how it went. |
DefTruth
commented
Nov 1, 2025
By using the _native_cudnn backend, I have currently avoided this problem |
sayakpaul
commented
Nov 1, 2025
Okay so it works. How about #12563? |
DefTruth
commented
Nov 1, 2025
also work |
sayakpaul
commented
Dec 6, 2025
Closing this since it has been resolved. |

What does this PR do?
Fix Context Parallelism: Implement Ring Attention Pattern for Coherent Multi-GPU Generation
🐛 Problem
I did some testings of the https://huggingface.co/docs/diffusers/main/training/distributed_inference on main branch.
Context parallelism in diffusers was producing fragmented/split images when using multiple GPUs. Instead of generating a single coherent image, each GPU was independently generating its own portion, resulting in visible seams or completely different content in each image segment.
Example: Running with
torchrun --nproc-per-node=2would produce an image that looked like two different images side-by-side rather than one unified image.🔍 Root Cause Analysis
The issue stems from how attention was computed in context parallel mode:
Before (Broken):
Each GPU was computing attention using only its local sequence chunk for Q, K, and V. This meant:
✅ Solution: Ring Attention Pattern
This PR implements the Ring Attention pattern where:
After (Fixed):
📝 Implementation Details
The fix is applied directly in the attention processors after rotary embeddings but before attention computation:
FluxAttnProcessor (
transformer_flux.py):🧪 Testing
For testing, run the following with torchrun --nproc-per-node=2:
Before Fix:
Result: Two different images side-by-side in output
After Fix:
Result: Single coherent image matching single-GPU output
Summary: This PR fixes context parallelism by ensuring each GPU's attention queries can access the full key-value context from all GPUs, implementing the Ring Attention pattern for coherent multi-GPU image generation.
Note:I have observed that some tensors in QwenImage cannot be divided by world_size
(encoder_hidden_states, encoder_hidden_mask etc.). I am also willing to make a new PR for the QwenImage support for context parallel by padding the tensors to be divisible by world size, similar to chengzeyi/ParaAttention#53 if you guys want to.Fixes # (issue)
Before submitting
documentation guidelines, and
here are tips on formatting docstrings.
Who can review?
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.
@sayakpaul@a-r-r-o-w