Distributed Training Setup with @parallelize#
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.
Basic Usage#
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()
model = MyModel().to(device)
model = tfoo.ddp.make_module_ddp(model)
for batch in dataloader:
...
if __name__ == "__main__":
train(cfg)
By default, @parallelize() uses all available GPUs,
falling back to a single CPU process if no GPUs are found.
Controlling World Size#
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.
Querying Distributed State#
Inside the decorated function, use get_rank() and
get_world_size() to query the current process:
@tfoo.dist.parallelize()
def train(cfg):
rank = tfoo.dist.get_rank() # 0 .. world_size-1
world_size = tfoo.dist.get_world_size()
if rank == 0:
print(f"Training on {world_size} GPUs")
For rank-0-only side effects (logging, saving checkpoints), use the
rank_zero_only() decorator:
@tfoo.dist.rank_zero_only
def save_checkpoint(model, path):
torch.save(model.state_dict(), path)
Wrapping a Model with DDP#
Use make_module_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.ddp.make_module_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.ddp.make_module_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())