AutoParallel automatically chooses sharding strategies for a PyTorch model, then applies them to produce a distributed module. Instead of manually deciding where to use FSDP, tensor parallelism, or intermediate redistributions, you provide a model, a device mesh, and example inputs; AutoParallel traces the joint forward/backward graph, solves for a low-cost sharding plan, and returns a parallelized module.
This guide is the best place to start if you are new to the project.
AutoParallel is currently most useful when all of the following are true:
- You already have a PyTorch
nn.Moduletraining workload. - You are comfortable with PyTorch distributed concepts such as
DeviceMeshand DTensor placements. - You want AutoParallel to choose among parameter sharding, activation sharding, and tensor parallel layouts for standard transformer-style computation.
- You are okay using experimental APIs and PyTorch nightly.
If you are trying to understand why the optimizer chose a specific strategy, read How AutoParallel Chooses a Strategy after this guide.
- Python 3.10+
- PyTorch nightly newer than 2.10
- CUDA GPUs for real execution
Install AutoParallel from GitHub:
pip install git+https://github.com/meta-pytorch/autoparallel.gitFor local development:
pip install -e .AutoParallel exposes two ways to use the library:
auto_parallel(...): the simpler API; best for first useAutoParallel(...): the full context-manager API; use this when you want to add constraints, inspect logs, or call optimizer utilities directly
Most newcomers should start with auto_parallel(...), then switch to the full
API when they need more control.
The easiest way to get a feel for the project is the HuggingFace example:
pip install transformers
python examples/example_hf.py --model gpt2 --mesh 8That example uses a fake process group so you can exercise the AutoParallel pipeline on a single machine without launching a real multi-process job. It is best thought of as a convenient single-process smoke test for tracing, optimization, and module construction, not as a real 8-rank training launch. A successful run ends with:
Forward + backward OK
The auto_parallel(...) helper takes:
- a model
- a
DeviceMesh - sample inputs for tracing
- the desired output sharding
import torch
from torch import nn
from torch.distributed.device_mesh import init_device_mesh
from torch.distributed.fsdp import MixedPrecisionPolicy
from torch.distributed.tensor import DTensor
from torch.distributed.tensor.placement_types import Replicate, Shard
from autoparallel import auto_parallel
class MLP(nn.Module):
def __init__(self, dim: int, hidden_dim: int):
super().__init__()
self.w1 = nn.Linear(dim, hidden_dim, bias=False)
self.w2 = nn.Linear(hidden_dim, dim, bias=False)
def forward(self, x):
return self.w2(torch.relu(self.w1(x)))
mesh = init_device_mesh("cuda", (4,), mesh_dim_names=("dp",))
with torch.device("meta"):
model = MLP(dim=1024, hidden_dim=4096)
local_batch = 8
seq_len = 128
sample_x = DTensor.from_local(
torch.randn(local_batch, seq_len, 1024),
mesh,
[Shard(0)],
)
parallel_model = auto_parallel(
model,
mesh,
sample_inputs=(sample_x,),
out_shardings=(Shard(0),),
mp_policy=MixedPrecisionPolicy(
param_dtype=torch.bfloat16,
reduce_dtype=torch.float32,
),
parameter_memory_budget=(None, None),
)
parallel_model.to_empty(device="cuda")
# Initialize or load weights here if the model was constructed on meta.
x = torch.randn(local_batch, seq_len, 1024, device="cuda")
out = parallel_model(x)
out.sum().backward()This example assumes a real 4-rank CUDA setup. In practice, that usually means
launching the script with torchrun on a machine with at least 4 visible GPUs.
If you only want a first smoke test on one machine without a real distributed
launch, use examples/example_hf.py, which sets up a fake process group for
you.
- The
DTensorsample input tells AutoParallel that the global input is sharded on batch dimension 0 across the mesh. out_shardings=(Shard(0),)asks for the output to stay batch-sharded.parameter_memory_budget=(None, None)applies the default parameter memory constraint through the simple API. This usually matters in training settings, because otherwise the optimizer may prefer to replicate parameters.parallel_model.to_empty(device="cuda")materializes the returned module on CUDA. If you built the original model on the meta device, you must then initialize or load its parameters before real execution.
Use the full AutoParallel API when you want explicit control over constraints
or verbose optimizer logs.
import torch
from torch import nn
from torch.distributed.device_mesh import init_device_mesh
from torch.distributed.fsdp import MixedPrecisionPolicy
from torch.distributed.tensor.placement_types import Replicate, Shard
from autoparallel import AutoParallel
class Block(nn.Module):
def __init__(self, dim: int, hidden_dim: int):
super().__init__()
self.w1 = nn.Linear(dim, hidden_dim, bias=False)
self.w2 = nn.Linear(hidden_dim, dim, bias=False)
def forward(self, x):
return self.w2(torch.relu(self.w1(x)))
mesh = init_device_mesh("cuda", (4,), mesh_dim_names=("dp",))
with torch.device("meta"):
model = Block(1024, 4096)
def input_fn():
global_batch = 32
seq_len = 128
return torch.randn(global_batch, seq_len, 1024, device="cuda")
mp_policy = MixedPrecisionPolicy(
param_dtype=torch.bfloat16,
reduce_dtype=torch.float32,
)
with AutoParallel(model, input_fn, mesh, mp_policy=mp_policy) as autop:
autop.add_input_constraints([(Shard(0),)])
autop.add_output_constraints([(Shard(0),)])
autop.add_parameter_memory_constraint(low=None, high=None)
sharding = autop.optimize_placement(verbose=True)
parallel_model = autop.apply_placement(sharding)
parallel_model.to_empty(device="cuda")
# Initialize or load weights here.The important distinction is that input_fn() returns global-shaped tensors,
while the resulting parallel module expects each rank to receive its local shard
at runtime.
This full-API example also assumes a real distributed run. If your mesh is
(4,), you typically launch 4 processes:
torchrun --nproc_per_node=4 my_script.pyFor a 2D mesh like (2, 4), the product of the mesh dimensions must match the
world size. On a single node, that would usually mean:
torchrun --nproc_per_node=8 my_script.pyAutoParallel does not launch processes for you; it assumes your distributed job has already been launched and that the mesh matches the active world size.
When you call optimize_placement(verbose=True), AutoParallel emits the
optimizer log through Python logging. If your script has not configured
logging, you may not see much output.
For ad hoc debugging, add:
import logging
logging.basicConfig(level=logging.INFO)Some examples in this repository use logging.DEBUG to show even more detail.
For a first experiment, use this checklist:
- Start with a small model or one of the examples.
- Use a 1D mesh first, usually with batch sharding on mesh dim 0.
- Constrain both inputs and outputs explicitly.
- Add a parameter memory constraint for training.
- Run
optimize_placement(verbose=True)and inspect the log. - Only after that, move to a 2D mesh or custom constraints.
- Basic Concepts: core ideas and terminology
- API Walkthrough: end-to-end lifecycle with both APIs
- Troubleshooting: common errors and what they usually mean
- FAQ: quick answers to common questions
- How AutoParallel Chooses a Strategy: deeper explanation of the optimizer
- Using
local_mapfor MoE and Custom Communication Patterns: advanced integration for dynamic communication patterns