1
0
mirror of https://github.com/huggingface/diffusers.git synced 2026-10-24 20:59:27 +03:00
Files
diffusers/docs/source/en/optimization/tpu.md
Jingya HUANG 1d5d056ecf [TPU] TorchTPU backend integration - eager / torch.compile / tp (#14039)
* feat: add torchtpu

* feat:draft TorchTPU support

* fix: wan overflow issue + compile mode error on sdxl

* doc: enhance with TorchTPU doc

* doc: enhance with TorchTPU doc

* style: remove unused imports flagged by ruff

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: remove Debug Eager and Fused Eager sections from tpu.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: add TP for TPU

* style: fix import sorting in TPU test scripts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* style: ruff format TPU test scripts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: style

* fix: test for native 4 devices

* fix: propagate TPU device fixes to Flux/Flux2/Wan-family copies; register tpu.md; drop redundant execution_device check

- Propagate the text_encoder.device-based fix (introduced for TPU CPU-offload
  support) from FluxPipeline/Flux2KleinPipeline/WanPipeline into their
  `# Copied from` copies (flux/*, flux2_klein_inpaint, visualcloze, anyflow,
  chronoedit, lucy_edit, skyreels_v2/*). SDXL-family copies of
  StableDiffusionXLPipeline.encode_prompt are intentionally left untouched;
  they'll be handled in a follow-up PR that fixes device placement for every
  pipeline component (not just text encoders).
- Register docs/source/en/optimization/tpu.md in _toctree.yml (was breaking
  the docs build: "not present in the table of contents").
- Remove the redundant "prefer non-CPU, non-meta component" loop from
  DiffusionPipeline._execution_device: PR #14383 already fixed this in
  DiffusionPipeline.device, which _execution_device falls back to. Verified
  on TPU hardware that _execution_device still resolves correctly for a
  split-placement pipeline after the removal.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* tests: cleanup

* fix: fix compile mode

* Update docs/source/en/optimization/tpu.md

Co-authored-by: Steven Liu <59462357+stevhliu@users.noreply.github.com>

* Update docs/source/en/optimization/tpu.md

Co-authored-by: Steven Liu <59462357+stevhliu@users.noreply.github.com>

* Update docs/source/en/optimization/tpu.md

Co-authored-by: Steven Liu <59462357+stevhliu@users.noreply.github.com>

* Update docs/source/en/optimization/tpu.md

Co-authored-by: Steven Liu <59462357+stevhliu@users.noreply.github.com>

* Update docs/source/en/optimization/tpu.md

Co-authored-by: Steven Liu <59462357+stevhliu@users.noreply.github.com>

* Update docs/source/en/optimization/tpu.md

Co-authored-by: Steven Liu <59462357+stevhliu@users.noreply.github.com>

* Update docs/source/en/optimization/tpu.md

Co-authored-by: Steven Liu <59462357+stevhliu@users.noreply.github.com>

* Update docs/source/en/optimization/tpu.md

Co-authored-by: Steven Liu <59462357+stevhliu@users.noreply.github.com>

* Update docs/source/en/optimization/tpu.md

Co-authored-by: Steven Liu <59462357+stevhliu@users.noreply.github.com>

* Update docs/source/en/optimization/tpu.md

Co-authored-by: Steven Liu <59462357+stevhliu@users.noreply.github.com>

* Update docs/source/en/optimization/tpu.md

Co-authored-by: Steven Liu <59462357+stevhliu@users.noreply.github.com>

* test: remove flux2 e2e test

* doc: apply suggestions

* doc: apply suggestions

* review: remove monkey patch

* review: revert neuron-specific changes in the tests

* review: apply suggestions

* Update docs/source/en/optimization/tpu.md

Co-authored-by: Steven Liu <59462357+stevhliu@users.noreply.github.com>

* doc: add tpu to tp doc

* review: remove unnecessary for tpu

* test: assert every _tp_plan parameter is sharded after a TP load

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* removal: delete redundant tp shard helpers

* removal: drop TPU workarounds no longer needed after recent main changes

* doc: keep the FLUX.2-dev text encoder off a single TPU chip in the TP example

* doc: encode the prompt on CPU in the TPU TP example so it runs end to end

* test: tighten TPU TP tolerance and simplify the TPU TP worker

* doc: use enable_model_cpu_offload() in the TPU eager example

* doc: shard both FLUX.2-dev text encoder and transformer in the TPU TP example

* doc: run the text encoders on TPU in the compiled example

* test: drop the redundant TPU sync in the TP worker

* test: trim comments in the TPU TP tests

* test: run the TPU TP test with mp.spawn like the CUDA one

* test: pin the TPU TP test to 4 chips

* Update docs/source/en/optimization/tpu.md

Co-authored-by: Steven Liu <59462357+stevhliu@users.noreply.github.com>

* doc: use DistributedConfig and dtype, and drop the torch_tpu import in the TPU examples

* test: skip the TPU TP test for models without a _tp_plan

* doc: correct the warning of recompilation

* fix: clone the SD3 timestep so torch.compile doesn't recompile every step, thus we can use it for the tpu.md compile mode snippet

* doc: update advice for compile mode when one chip doesn't fit

* test: pass the TPU TP tolerances as test arguments

* review: apply suggestions to the doc

* review: view compilation patch for tpu-only

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: JingyaHuang <huang_jingya@outlok.com>
Co-authored-by: Steven Liu <59462357+stevhliu@users.noreply.github.com>
Co-authored-by: Sayak Paul <spsayakpaul@gmail.com>
2026-10-09 16:16:23 +05:30

6.6 KiB

TorchTPU

TorchTPU is a PyTorch backend for Google's Tensor Processing Units (TPUs), which lets you run Diffusers pipelines on Cloud TPUs (v6e, v7x) with minimal code changes.

Two execution modes are available:

Mode Constant How to activate Notes
Strict eager (default) EagerMode.DEFER_NEVER pipe.to("tpu") Operations dispatched one at a time, asynchronous
Compile — torch.compile(module, backend="tpu") AOT compilation with TpuBackend

Follow the TorchTPU installation guide. Once installed, import torch loads it automatically and registers the "tpu" device, so pipe.to("tpu") is the only change needed. Add import torch_tpu only if you disabled backend autoloading with TORCH_DEVICE_BACKEND_AUTOLOAD=0.

Eager mode

FLUX.1-schnell doesn't fit on a single v6e chip all at once, so use [~DiffusionPipeline.enable_model_cpu_offload] to move each model to the TPU only while it runs. It detects the "tpu" device automatically.

import torch

from diffusers import FluxPipeline

pipe = FluxPipeline.from_pretrained("black-forest-labs/FLUX.1-schnell", dtype=torch.bfloat16)
pipe.enable_model_cpu_offload()

image = pipe(
    prompt="a golden retriever surfing a wave, photorealistic",
    height=1024,
    width=1024,
    num_inference_steps=4,
    guidance_scale=0.0,
).images[0]

image.save("output.png")

If a model is too large for a single chip, or you have several chips and want lower latency, shard the models across chips instead. See the Tensor parallelism section.

Compiled mode

TorchTPU registers "tpu" as a torch.compile backend name (TpuBackend under the hood), so components compile like any other torch.compile target. The first call (warmup) is slow because it compiles; later calls with the same shapes reuse the compiled graph.

Important

TorchTPU requires static shapes, so pass dynamic=False. A new height or width compiles again for that shape, once; shapes already seen are reused. Changing num_inference_steps doesn't recompile.

When the whole pipeline fits on one chip, move it to the TPU and compile the full transformer. Stable Diffusion 3.5 Medium (~15GB in bf16) fits on a single v6e chip.

import torch

from diffusers import StableDiffusion3Pipeline

pipe = StableDiffusion3Pipeline.from_pretrained("stabilityai/stable-diffusion-3.5-medium", dtype=torch.bfloat16)
pipe.to("tpu")
pipe.transformer.compile(backend="tpu", fullgraph=True, dynamic=False)

# Warmup — triggers static graph compilation.
pipe(prompt="warmup", height=1024, width=1024, num_inference_steps=40, guidance_scale=4.5)

# Later calls with the same shapes reuse the compiled graph.
image = pipe(
    prompt="a golden retriever surfing a wave, photorealistic",
    height=1024,
    width=1024,
    num_inference_steps=40,
    guidance_scale=4.5,
).images[0]

image.save("output.png")

If the pipeline doesn't fit on one chip:

  • With several chips, shard it with tensor parallelism instead. Everything stays on the TPU, and pipe.transformer.compile(...) works the same way on the sharded transformer.
  • With [~DiffusionPipeline.enable_model_cpu_offload], the offload hooks can't be traced by torch.compile. Compile only the transformer's repeated blocks instead, with pipe.transformer.compile_repeated_blocks(backend="tpu", fullgraph=True, dynamic=False).

Tensor parallelism

Shard models too large for one chip across several. FLUX.2-dev's text encoder (~48GB) and transformer (~64GB) each exceed a single chip, so the example below shards both:

  • the transformer with [TensorParallelConfig], passed to the parallel_config argument of [~ModelMixin.from_pretrained]. Each rank reads only its own slice of every sharded weight, so the full model is never materialized. For general TP details (_tp_plan, colwise/rowwise), see the Tensor parallelism guide.
  • the text encoder with Transformers' own tensor parallelism, passing a [~transformers.DistributedConfig] and the same mesh.

On TPU, initialize the process group with backend="tpu_dist" and build the mesh with DeviceMesh("tpu", ...).

import torch
import torch.distributed as dist
from torch.distributed.device_mesh import DeviceMesh
from transformers import DistributedConfig, Mistral3ForConditionalGeneration

from diffusers import Flux2Pipeline, Flux2Transformer2DModel, TensorParallelConfig

dist.init_process_group(backend="tpu_dist")
mesh = DeviceMesh("tpu", list(range(dist.get_world_size())))

repo_id = "black-forest-labs/FLUX.2-dev"
text_encoder = Mistral3ForConditionalGeneration.from_pretrained(
    repo_id,
    subfolder="text_encoder",
    dtype=torch.bfloat16,
    distributed_config=DistributedConfig(tp_plan="auto"),
    device_mesh=mesh,
)
transformer = Flux2Transformer2DModel.from_pretrained(
    repo_id, subfolder="transformer", dtype=torch.bfloat16, parallel_config=TensorParallelConfig(mesh=mesh)
)
pipe = Flux2Pipeline.from_pretrained(
    repo_id, text_encoder=text_encoder, transformer=transformer, dtype=torch.bfloat16
)
pipe.vae.to("tpu")

image = pipe(
    prompt="a golden retriever surfing a wave, photorealistic",
    num_inference_steps=28,
    generator=torch.Generator("cpu").manual_seed(0),
).images[0]
if dist.get_rank() == 0:
    image.save("output.png")

Launch one process per chip. Set --nproc_per_node to use all the number of TPU chips on your host.

eval $(python -m torch_tpu._internal.distributed.launchers.singlehost_wrapper | sed 's/^/export /')
torchrun --nproc_per_node=8 flux2_tp.py

Warning

The launch command above uses a TorchTPU internal module (torch_tpu._internal), not a stable API, and is expected to change in a future TorchTPU release. Check the TorchTPU repository for the current way to launch multi-chip jobs.