Easy Distributed Training Setup#
Spawning Parallel Processes#
The parallelize() decorator is the simplest way to
run a training function across multiple GPUs/CPUs. It handles process spawning,
distributed setup, and cleanup automatically — you only write the single-process
logic.
Decorate your training function and call it normally from the main process:
import torchfoo as tfoo
@tfoo.dist.parallelize()
def train(cfg):
rank = tfoo.dist.get_rank()
world_size = tfoo.dist.get_world_size()
device = tfoo.current_device()
...
if __name__ == "__main__":
train(cfg)
By default, @parallelize() uses all available GPUs,
falling back to a single CPU process if no GPUs are found.
Inside the parallelized function, you can use
get_rank(),
get_world_size(),
to query the distributed state, and
current_device()
to get the torch.device corresponding to the current process.
The torch.distributed.get_rank() etc. from base PyTorch still work,
these ones are just nicer as they do not fail in non-distributed mode.
So, the same code works for single- and multi-process settings.
Running this script does not require anything special. Just call your train script like normal.
python <script>.py
Wrapping a Model with DDP#
Use make_ddp() to wrap your model. It converts
BatchNorm layers to SyncBatchNorm and wraps with
DistributedDataParallel automatically. It is a no-op outside of distributed
mode, so the same code works for both single- and multi-GPU runs:
@tfoo.dist.parallelize()
def train(cfg):
device = tfoo.current_device()
model = MyModel().to(device)
model = tfoo.module.make_ddp(model) # no-op if world_size == 1
...
Full Example#
import torch
import torchfoo as tfoo
from dataclasses import dataclass
@dataclass
class Config:
epochs: int = 10
lr: float = 1e-3
@tfoo.dist.parallelize()
def train(cfg: Config):
rank = tfoo.dist.get_rank()
device = tfoo.current_device()
model = torch.nn.Linear(128, 10).to(device)
model = tfoo.module.make_ddp(model)
optimizer = torch.optim.Adam(model.parameters(), lr=cfg.lr)
for epoch in range(cfg.epochs):
# ... training loop ...
if rank == 0:
print(f"Epoch {epoch} done")
if __name__ == "__main__":
train(Config())
Extras#
Controlling World Size with @parallelize#
Pass world_size to use a specific number of processes:
@tfoo.dist.parallelize(world_size=2)
def train(cfg):
...
If world_size exceeds the number of available GPUs, the gloo backend is
used automatically and all processes run on CPU.