Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
89 commits
Select commit Hold shift + click to select a range
98f6c8c
draft:add neuron as a legit backend
JingyaHuang Mar 18, 2026
c58b8b8
Merge branch 'huggingface:main' into add-neuron-backend
JingyaHuang Mar 18, 2026
3367409
Merge branch 'huggingface:main' into add-neuron-backend
JingyaHuang Mar 19, 2026
0c51734
Merge branch 'main' into add-neuron-backend
JingyaHuang Mar 25, 2026
a76953c
feat: neuron-specific changes in the pipeline
JingyaHuang Mar 26, 2026
2480388
tests: eager tests
JingyaHuang Mar 27, 2026
1469c04
draft: start with tp for flux2
JingyaHuang Apr 9, 2026
929ab72
fix: style
JingyaHuang Apr 9, 2026
52cac76
Merge branch 'huggingface:main' into add-neuron-backend
JingyaHuang Apr 9, 2026
30cb353
Merge branch 'huggingface:main' into support-neuron-tp
JingyaHuang Apr 9, 2026
28a5086
Merge branch 'add-neuron-backend' of github.com:JingyaHuang/diffusers…
JingyaHuang Apr 9, 2026
7fab0c4
Merge branch 'huggingface:main' into support-neuron-tp
JingyaHuang Apr 10, 2026
68689e5
Merge branch 'huggingface:main' into add-neuron-backend
JingyaHuang Apr 10, 2026
da79308
Merge branch 'main' into add-neuron-backend
JingyaHuang Apr 10, 2026
3bb9c7c
fix:apr_02 beta
JingyaHuang Apr 10, 2026
c4facab
Merge branch 'add-neuron-backend' of github.com:JingyaHuang/diffusers…
JingyaHuang Apr 10, 2026
dff1f32
feat:add wan
JingyaHuang Apr 10, 2026
1c930c4
Merge branch 'huggingface:main' into support-neuron-tp
JingyaHuang Apr 13, 2026
1eb5ff9
Merge branch 'huggingface:main' into add-neuron-backend
JingyaHuang Apr 13, 2026
cbe8f28
fix:pixart
JingyaHuang Apr 14, 2026
16b9606
fix: rewrite flux swiglu activation to avoid gather op in neuron IR
JingyaHuang Apr 15, 2026
7f13f68
test: pixart compile mode on neuron
JingyaHuang Apr 15, 2026
a46cb19
Merge branch 'main' into neuron-torch-comppile
JingyaHuang Apr 22, 2026
a354b88
cleanup & fix style
JingyaHuang May 11, 2026
931bb85
Merge branch 'neuron-torch-comppile' into support-neuron-tp
JingyaHuang May 11, 2026
9ab6dc3
Merge branch 'main' into support-neuron-tp
JingyaHuang May 11, 2026
48fb75b
Merge branch 'main' into support-neuron-tp
JingyaHuang Jun 22, 2026
c350f7b
merge: another change
JingyaHuang Jun 22, 2026
644477a
Merge branch 'main' into support-neuron-tp
JingyaHuang Jun 22, 2026
03cb725
review: cleanup+suggestions
JingyaHuang Jun 23, 2026
9da93ed
Merge branch 'support-neuron-tp' of github.com:JingyaHuang/diffusers …
JingyaHuang Jun 23, 2026
d44f772
fix: CIs style
JingyaHuang Jun 24, 2026
3fc043e
Merge branch 'main' into support-neuron-tp
JingyaHuang Jun 24, 2026
e6d20d8
tests: add test units for tp
JingyaHuang Jun 24, 2026
e76a2fc
Merge branch 'support-neuron-tp' of github.com:JingyaHuang/diffusers …
JingyaHuang Jun 24, 2026
034ba9e
fix: in case of text-encoder(s) on CPU
JingyaHuang Jun 24, 2026
4907524
review:cleanup+add test
JingyaHuang Jun 25, 2026
af2aed7
Merge branch 'support-neuron-tp' of github.com:JingyaHuang/diffusers …
JingyaHuang Jun 25, 2026
b9b048b
Merge branch 'main' into support-neuron-tp
JingyaHuang Jun 25, 2026
915eeb1
fix: style
JingyaHuang Jun 25, 2026
720dad2
Merge branch 'support-neuron-tp' of github.com:JingyaHuang/diffusers …
JingyaHuang Jun 25, 2026
89cf8b6
doc: remove it for now
JingyaHuang Jun 25, 2026
29cd9c3
Add from_single_file support for SkyReelsV2 and ChronoEdit transforme…
HaozheZhang6 Jun 25, 2026
eaab299
multi-GPU VAE Fix for Cosmos 3 (#13924)
atharvajoshi10 Jun 25, 2026
30a43d5
docs: fix repeated word typo in set_timesteps docstring (#13876)
ramkumar27072006 Jun 26, 2026
155802c
clean some stuff to simplify code.
sayakpaul Jun 26, 2026
f133732
Merge branch 'main' into JingyaHuang-support-neuron-tp
sayakpaul Jun 26, 2026
b3d8130
clean more to remove permutation related shenanigans.
sayakpaul Jun 27, 2026
7ea75f7
revert: put torch.chunk back
JingyaHuang Jul 1, 2026
c73cf09
Merge branch 'main' into support-neuron-tp
JingyaHuang Jul 1, 2026
eb58402
Merge branch 'main' into support-neuron-tp
JingyaHuang Jul 2, 2026
c3e123c
Update docs/source/en/training/distributed_inference.md
JingyaHuang Jul 2, 2026
dc33e26
Merge Sayak's TP simplification (PR #1): generic Packed{Col,Row}wiseP…
JingyaHuang Jul 2, 2026
491c537
Merge branch 'support-neuron-tp' of github.com:JingyaHuang/diffusers …
JingyaHuang Jul 2, 2026
c5b6c89
Address Sayak's TP review nits (docstrings + stale comments)
JingyaHuang Jul 2, 2026
310c471
Fix stale permuter reference in Flux2ParallelSelfAttnProcessor comment
JingyaHuang Jul 2, 2026
909dfcf
Derive TP backend inside apply_tensor_parallel from the mesh device type
JingyaHuang Jul 2, 2026
70212e5
Simplify TP backend derivation to the mesh device type alone
JingyaHuang Jul 2, 2026
c130e00
Remove incorrect "Neuron reports as cpu" rationale from TP backend do…
JingyaHuang Jul 2, 2026
b0b3b7c
Drop stale "XLA" labels from Neuron device/runtime comments
JingyaHuang Jul 2, 2026
4f2fea5
make style: reflow tensor_parallel docstrings
JingyaHuang Jul 2, 2026
93dae9c
Revert "revert: put torch.chunk back"
JingyaHuang Jul 7, 2026
4bb881d
fix: sharding for neuron + validate flux 1
JingyaHuang Jul 7, 2026
1ba454f
Merge branch 'main' into support-neuron-tp
JingyaHuang Jul 7, 2026
d2733b4
fix: change for check_repository_consistency
JingyaHuang Jul 7, 2026
44eba6e
Merge branch 'support-neuron-tp' of github.com:JingyaHuang/diffusers …
JingyaHuang Jul 7, 2026
5f661c6
Merge branch 'main' into support-neuron-tp
JingyaHuang Jul 8, 2026
06f7976
feat: support qwen image as well
JingyaHuang Jul 8, 2026
911c58e
Merge branch 'support-neuron-tp' of github.com:JingyaHuang/diffusers …
JingyaHuang Jul 8, 2026
b390132
Merge branch 'main' into support-neuron-tp
JingyaHuang Jul 8, 2026
4b7ae6b
Update docs/source/en/training/distributed_inference.md
JingyaHuang Jul 10, 2026
6d84695
Merge branch 'main' into support-neuron-tp
JingyaHuang Jul 10, 2026
c6e0680
review: keep bria and nucleusmoe out
JingyaHuang Jul 10, 2026
1646814
review: keep bria and nucleusmoe out
JingyaHuang Jul 10, 2026
2218453
review: keep bria and nucleusmoe out
JingyaHuang Jul 10, 2026
3613423
review: address agent review
JingyaHuang Jul 10, 2026
e8dcefc
Merge branch 'main' into support-neuron-tp
JingyaHuang Jul 10, 2026
d55ac18
review: apply suggestions
JingyaHuang Jul 16, 2026
48ea484
Merge branch 'main' into support-neuron-tp
JingyaHuang Jul 16, 2026
fcc2bcb
Merge branch 'main' into support-neuron-tp
JingyaHuang Jul 17, 2026
2364649
Merge branch 'main' into support-neuron-tp
JingyaHuang Jul 27, 2026
ad5536d
Merge branch 'main' into support-neuron-tp
sayakpaul Jul 28, 2026
737945a
Update docs/source/en/training/distributed_inference.md
JingyaHuang Jul 28, 2026
686e379
review: change example tp size to 4
JingyaHuang Jul 28, 2026
5c2549d
Merge branch 'support-neuron-tp' of github.com:JingyaHuang/diffusers …
JingyaHuang Jul 28, 2026
57365c5
review: add flux tp test
JingyaHuang Jul 28, 2026
3c80014
review: address comments
JingyaHuang Aug 4, 2026
31112cb
Merge branch 'main' into support-neuron-tp
JingyaHuang Aug 4, 2026
6f3db51
Merge branch 'main' into support-neuron-tp
sayakpaul Aug 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/source/en/api/parallel.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,9 @@ Parallelism strategies help speed up diffusion transformers by distributing comp
[[autodoc]] ContextParallelConfig

[[autodoc]] hooks.apply_context_parallel

## TensorParallelConfig

[[autodoc]] TensorParallelConfig

[[autodoc]] hooks.apply_tensor_parallel
130 changes: 130 additions & 0 deletions docs/source/en/training/distributed_inference.md
Original file line number Diff line number Diff line change
Expand Up @@ -431,3 +431,133 @@ pipeline = DiffusionPipeline.from_pretrained(
CKPT_ID, transformer=transformer, dtype=torch.bfloat16,
).to(device)
```

## Tensor parallelism

[Tensor parallelism](https://huggingface.co/spaces/nanotron/ultrascale-playbook?section=tensor_parallelism) shards the weight matrices of a model across devices. Each device holds a column-wise (`"colwise"`) or row-wise (`"rowwise"`) slice of each layer, computes a partial result, and an `AllReduce`/`AllGather` at the layer boundary reconstructs the full output. Unlike context parallelism, it reduces the per-device *weight* memory, which is useful for models that do not fit on a single device.

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 we also provide a short section after the TP section, showing which parallelism should be preferred in what circumstances?

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.

Also, should there be guidance for model authors on how they should write the _tp_plan for their model? That seems non-trivial to me and some useful hints could be nice to have there.

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 guidance on which parallelism config to use, I think each section already explains briefly the trade-offs , eg. CP -> long sequences, TP -> large weights. I just added a section to summarize i bit, If we want more precise/actionable guide, we need to run experiments, since the right choice also depends on the specific model.

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.

Will add more explanation on contributing tp plan tho!

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.

I think all of that makes sense and should be documented (for example, the right choices depending on the model size, input problem space, etc.).


Pass a [`TensorParallelConfig`] to [`~ModelMixin.enable_parallelism`]. `tp_degree` is the number of devices to shard across and must divide the model's number of attention heads. The model must define a `_tp_plan` (a flat mapping of module-name globs to a `"colwise"`/`"rowwise"` style).

```py
import torch
from torch import distributed as dist
from diffusers import DiffusionPipeline, TensorParallelConfig

def setup_distributed():
if not dist.is_initialized():
dist.init_process_group(backend="nccl")
rank = dist.get_rank()
device = torch.device(f"cuda:{rank}")
torch.cuda.set_device(device)
return device

def main():
Comment thread
JingyaHuang marked this conversation as resolved.
device = setup_distributed()
world_size = dist.get_world_size()

pipeline = DiffusionPipeline.from_pretrained(
"black-forest-labs/FLUX.2-dev", torch_dtype=torch.bfloat16
) # weights stay on CPU

# Shard the transformer first, then move only each rank's slice onto the accelerator.
pipeline.transformer.enable_parallelism(config=TensorParallelConfig(tp_degree=world_size))
pipeline.transformer.to(device)

# Move the remaining, non-sharded components onto the accelerator individually.
pipeline.text_encoder.to(device)
pipeline.vae.to(device)

generator = torch.Generator().manual_seed(42)
image = pipeline(prompt="a cat holding a sign that says hello", generator=generator).images[0]
if dist.get_rank() == 0:
image.save("output.png")
if dist.is_initialized():
dist.destroy_process_group()

if __name__ == "__main__":
main()
```

```shell
torchrun --nproc-per-node 4 tensor_parallel_flux.py
```

`tp_degree` is taken from `world_size` above, so `--nproc-per-node 4` shards the transformer across 4 devices.

### Writing a _tp_plan

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.

@stevhliu can this first _ be escaped so that it renders properly? If so, how?


Tensor parallelism only works on models that define a `_tp_plan`, a flat class attribute mapping module-name globs to a sharding style. Writing one is mostly a matter of pairing each projection that *expands* the hidden dimension with the projection that *contracts* it back.

Each key may contain **at most one `*`**, and the prefix before it must resolve to an [`nn.ModuleList`](https://pytorch.org/docs/stable/generated/torch.nn.ModuleList.html) so a single entry covers every block. A key without a `*` applies to the model itself. Paths are relative to the model.

#### Colwise and rowwise
Comment thread
JingyaHuang marked this conversation as resolved.

| Style | Shards | Each rank | Use for |
|---|---|---|---|
| `"colwise"` | output features (`weight` dim 0) | computes a slice of the output | `to_q`, `to_k`, `to_v`, FFN in-projection |
| `"rowwise"` | input features (`weight` dim 1) | computes a partial sum | `to_out.0`, FFN out-projection |

Always pair them in that order. A `"colwise"` projection leaves its output sharded, the following `"rowwise"` projection consumes that shard directly, and a single `AllReduce` at the block boundary reconstructs the result. Sharding the pair any other way forces a gather in the middle and communicates far more.

For attention this means each rank owns a subset of heads, which is why `tp_degree` must divide the head count. Encoder-stream duplicates (`add_q_proj`, `to_add_out`, `ff_context`) follow the same pattern as their image-stream counterparts.

#### Fused projections

When one `Linear` packs several logical tensors along the dimension being sharded, plain `"colwise"`/`"rowwise"` slices straight across the concatenation and misaligns the pieces. Use `PackedColwiseParallel`/`PackedRowwiseParallel` instead, which shard each packed block independently.

```py
# in src/diffusers/models/transformers/your_model.py
from ...hooks.tensor_parallel import PackedColwiseParallel, PackedRowwiseParallel
```

`blocks` is a list of proportional integers whose sum divides the packed dimension — `[1, 1]` for a SwiGLU gate+up projection of equal halves, or `[1, 1, 1, 3, 3]` for a fused Q+K+V+gate+up projection with `mlp_ratio=3`.

```py
"transformer_blocks.*.ff.linear_in": PackedColwiseParallel([1, 1]),
```

When the block sizes are only known from the config, omit the argument and store the absolute sizes on the `Linear` during `__init__` instead, as `_tp_packed_col_blocks` or `_tp_packed_row_blocks`.

```py
# in the attention module's __init__
self.to_out._tp_packed_row_blocks = [self.inner_dim, self.mlp_hidden_dim]
```

```py
# in the model's _tp_plan
"single_transformer_blocks.*.attn.to_out": PackedRowwiseParallel(),
```

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.

(nit): Perhaps provide a reference implementation example of Flux2 (by linking the path to the modeling file)?

#### What to leave out

Anything absent from the plan stays replicated on every rank, which is the right choice for normalization layers, AdaLN modulation (`img_mod`/`txt_mod`), patch and text embeddings, and the final `norm_out`/`proj_out`. These are small, so sharding them saves little memory while adding communication.

#### Constraints and verification

- `tp_degree` must divide `config.num_attention_heads`. This is validated in [`~ModelMixin.enable_parallelism`].
- Every packed block must *individually* be divisible by `tp_degree`, not just their sum.

Validate a new plan numerically rather than by eye: generate with a fixed seed on a single device, then again under tensor parallelism, and compare the outputs. A misplaced `"colwise"`/`"rowwise"` usually still runs and produces a plausible but wrong image.

> [!TIP]
> Start from an existing plan for a similar architecture. [`QwenImageTransformer2DModel`] is fully unfused and every entry is plain `"colwise"`/`"rowwise"`, [`FluxTransformer2DModel`] adds a single packed row-wise projection, and [`Flux2Transformer2DModel`] covers both packed styles.

## Choosing a strategy

The strategies above solve different problems, and the useful question is not which is fastest in the abstract but what you are running out of.

| Strategy | Splits | Reduces | Latency for one prompt | Best when |
|---|---|---|---|---|
| [Accelerate](#accelerate) / [DDP](#pytorch-distributed) | prompts across replicas | nothing — each device holds a full copy | unchanged | the model already fits and you have many prompts |
| [`device_map`](#device_map) | components across devices | weight memory | slightly worse | the model doesn't fit and the interconnect is slow |
| [Context parallelism](#context-parallelism) | the input sequence | activation memory | lower | sequences are long — high resolution or video |
| [Tensor parallelism](#tensor-parallelism) | weight matrices | weight memory | lower | one component's weights don't fit and the interconnect is fast |

Some practical guidance:

- **Throughput on many prompts, model already fits.** Use data parallelism. It is the only strategy here that scales throughput linearly without touching the model, and it leaves single-prompt latency alone.
- **A single component's weights don't fit.** Reach for tensor parallelism first, since it lowers both memory and latency. It communicates at every block boundary, so it wants a fast interconnect like NVLink; over PCIe that per-layer traffic can outweigh the compute it saves, and `device_map` becomes the better choice. `device_map` also handles the case where the components are individually fine but collectively too large.
- **Activations, not weights, are the problem.** This is the long-sequence regime — large images, many frames — and context parallelism is the direct answer. For picking a backend within it, see the [Ulysses/Ring benchmarks](#ulysses-attention) above; Ulysses gives the best throughput but caps at the attention head count, and unified attention lifts that cap once you have at least 4 devices.
- **Both weights and sequence are too large.** Combine tensor and context parallelism. [`TensorParallelConfig`] accepts a `mesh` argument so both can share one device mesh.

Two constraints often decide this before performance does: tensor parallelism requires the model to define a [`_tp_plan`](#writing-a-_tp_plan), and its `tp_degree` must divide the attention head count. Context parallelism has no such per-model requirement and works with most attention backends.
2 changes: 2 additions & 0 deletions src/diffusers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@
"StableCascadeUNet",
"T2IAdapter",
"T5FilmDecoder",
"TensorParallelConfig",
"Transformer2DModel",
"TransformerTemporalModel",
"UNet1DModel",
Expand Down Expand Up @@ -1177,6 +1178,7 @@
StableAudioDiTModel,
T2IAdapter,
T5FilmDecoder,
TensorParallelConfig,
Transformer2DModel,
TransformerTemporalModel,
UNet1DModel,
Expand Down
1 change: 1 addition & 0 deletions src/diffusers/hooks/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@
from .pyramid_attention_broadcast import PyramidAttentionBroadcastConfig, apply_pyramid_attention_broadcast
from .smoothed_energy_guidance_utils import SmoothedEnergyGuidanceConfig
from .taylorseer_cache import TaylorSeerCacheConfig, apply_taylorseer_cache
from .tensor_parallel import apply_tensor_parallel
from .text_kv_cache import TextKVCacheConfig, apply_text_kv_cache
Loading
Loading