distributed#
Utility functions for using torch.distributed.
Classes
A convenient wrapper around torch.distributed.ProcessGroup objects. |
|
This process's place in a run: its rank, the run's size, and the device it computes on. |
|
How a model is distributed over the ranks of a run. |
|
A class to manage various parallel groups such as data parallel, tensor parallel etc. |
Functions
Return |
|
Returns the distributed backend. |
|
Synchronizes all processes. |
|
Return |
|
Build an |
|
Own the run's process group for the block, and yield this process's state. |
|
How |
|
FSDP2-shard a HF causal LM's decoder layers, and the root unless |
|
Gathers byte strings to global rank |
|
Gathers |
|
Returns whether the distributed package is available. |
|
True if launched distributed (WORLD_SIZE>1), readable BEFORE the process group is set up. |
|
True if a real multi-rank job is running: the process group is initialized and world size > 1. |
|
Return True if any submodule of |
|
Returns whether the distributed package is initialized. |
|
Returns whether the current process is the master process. |
|
Returns the rank of the current process. |
|
Record that |
|
Wrap a DataLoader so each rank sees a unique shard. |
|
Returns the number of processes. |
- class DistributedProcessGroup#
Bases:
objectA convenient wrapper around torch.distributed.ProcessGroup objects.
- __init__(group=None)#
Initialize the distributed process group.
- Parameters:
group (ProcessGroup | int | None)
- static get_dist_syncd_obj(obj, groups, op)#
Get the distributed synchronized object across the specified distributed groups.
- Parameters:
obj (Any)
groups (DistributedProcessGroup | list[DistributedProcessGroup])
op (Callable)
- is_initialized()#
Check if the distributed process group is initialized.
- Return type:
bool
- rank()#
Get the rank of the current process group.
- Return type:
int
- world_size()#
Get the world size of the current process group.
- Return type:
int
- class DistributedState#
Bases:
objectThis process’s place in a run: its rank, the run’s size, and the device it computes on.
distributed_run()yields it, for a single process too (rank 0 of a run of one).- __init__(rank, world_size, device)#
- Parameters:
rank (int)
world_size (int)
device (device | str)
- Return type:
None
- device: device | str#
- property is_main: bool#
Whether this is rank 0, the rank that writes the run’s outputs.
- rank: int#
- world_size: int#
- class DistributionPlan#
Bases:
objectHow a model is distributed over the ranks of a run.
world_size– the ranks that hold the model;1for a single process.ep_size– the expert-parallel degree: an MoE’s routed experts are sharded over groups ofep_sizeranks.1shards none.dense– how the rest of the model, all of it without expert parallelism, is placed:"replicate"gives every rank a whole copy,"fsdp"FSDP2-shards it.
Every rank calibrates on its own data, so data parallelism spans the whole world, and each expert shard is replicated
edp = world_size / ep_sizetimes.- __init__(world_size=1, ep_size=1, dense='replicate')#
- Parameters:
world_size (int)
ep_size (int)
dense (Literal['replicate', 'fsdp'])
- Return type:
None
- dense: Literal['replicate', 'fsdp'] = 'replicate'#
- property edp: int#
how many ranks hold a replica of each expert shard.
- Type:
Expert-data-parallel degree
- ep_size: int = 1#
- property is_expert_parallel: bool#
Whether routed experts are sharded across ranks.
- property is_fsdp_dense: bool#
Whether the dense part is FSDP2-sharded rather than replicated.
- world_size: int = 1#
- class ParallelState#
Bases:
objectA class to manage various parallel groups such as data parallel, tensor parallel etc.
Specify the parallel groups of type
torch.distributed.ProcessGroupfor the current module. If the parallel group is not used, it should be set to -1. if a parallel group is None, it will use the default PyTorch distributed process group which is the whole world.- __init__(data_parallel_group=None, tensor_parallel_group=-1, expert_model_parallel_group=-1)#
Initialize the parallel state.
- Parameters:
data_parallel_group (ProcessGroup | int | None)
tensor_parallel_group (ProcessGroup | int | None)
expert_model_parallel_group (ProcessGroup | int | None)
- all_reduce(label, fn, combine)#
Return
combineof every rank’sfn(), on every rank.combinetakes the ranks’ values in rank order and returns the one every rank gets, e.g.minfor a batch size each rank’s memory bounds, where one rank’s choice could run another out of memory. Iffnraises on any rank, every rank raises, instead of waiting in the gather that rank never joins. Ranks reducing different labels at the same point fail. Collective; outside a distributed runfnruns alone.- Parameters:
label (str)
fn (Callable[[], _V])
combine (Callable[[list[_V]], _V])
- Return type:
_V
- backend()#
Returns the distributed backend.
- Return type:
str | None
- barrier(group=None)#
Synchronizes all processes.
- Return type:
None
- broadcast_from(label, fn, rank=0)#
Return
fn()run onrankonly, on every rank.For a value that depends on what each rank reads for itself – the environment, its GPU, files on its node – so that ranks computing it for themselves could disagree. If
fnraises, every rank raises, instead of waiting in a broadcastranknever sends. Ranks broadcasting different labels at the same point fail. Collective: every rank calls it at the same point, with the samerank. Outside a distributed runfnruns alone.- Parameters:
label (str)
fn (Callable[[], _V])
rank (int)
- Return type:
_V
- distributed_forward_loop(model, dataloader, device=None, ep_mesh=None)#
Build an
mtq.quantizeforward_loopfor a distributed (e.g. expert-parallel) model.Moves each batch onto
device, when given, and runs the capturedmodelforward (the EP all-reduce / all-to-all collectives fire inside that forward). A model whose plan (distribution_plan()) is expert-parallel passes theep_meshits experts are sharded over, for the all-to-all forwards that quantizer insertion drops to be re-installed before the first forward; any other model passes none.mtq.quantizehands theforward_loopcallback an inner module that this closure ignores, calling the captured top-levelmodelinstead so the full distributed forward (and its collectives) runs.KV/state caching is disabled for the duration (via
_disable_use_cache), mirroring the single-node_forward_loop. This distributed path previously omitted the guard, so it ran calibration atconfig.use_cache(defaultTrue); for hybrid Mamba/attention models (e.g. NemotronH) the cache is mutated in-place, which both wastes memory and breaks calibration correctness.
- distributed_run(timeout=None, device='cuda')#
Own the run’s process group for the block, and yield this process’s state.
On a distributed launch (
WORLD_SIZE>1) this sets the process group up, binding this rank’s CUDA device, withtimeout. It yields this rank’sDistributedState, on that CUDA device. A single process sets nothing up and is rank 0 of a run of one, ondevice.On exit, even if the body raises, it destroys the group the run set up: the launch’s, or one the body set up, such as the one-rank group a single process’s FSDP2 load shards through. A group that was already there when the run began is the caller’s, and is left as it is. A rank whose body raised in a run of several ranks exits through
abort()instead, and the launcher tears the job down: destroying the group would wait on peers that may sit in a collective this rank never reaches.- Parameters:
timeout (timedelta | None)
device (device | str)
- distribution_plan(model)#
How
modelis distributed: the plan recorded on it (set_distribution_plan()).A model with no plan recorded is a single process’s, unless it shows otherwise: one that is FSDP2-sharded, or held in a run of several ranks, raises rather than be taken for one.
- Parameters:
model (Module)
- Return type:
- fsdp2_wrap(model, shard_root=True, mp_policy=None, cpu_offload=False)#
FSDP2-shard a HF causal LM’s decoder layers, and the root unless
shard_root=False.It loads a model, so it lives with the loaders now:
modelopt.torch.utils.plugins.model_load_utils.fsdp2_wrap(). This keeps the old path.- Parameters:
cpu_offload (bool)
- gather_bytes(data, dst, group=None, chunk_bytes=268435456)#
Gathers byte strings to global rank
dstonly, without unpickling anything on the way.Unlike
torch.distributed.gather_objectit never runs pickle, and it moveschunk_bytesper rank at a time to bound device memory. Returns the per-rank bytes ondstandNoneelsewhere.- Parameters:
data (bytes)
dst (int)
chunk_bytes (int)
- Return type:
list[bytearray] | None
- gather_state_dicts(state, dst, group=None)#
Gathers
statefrom every rank ofgroupto global rankdst, loaded withweights_only.Yields one rank’s dict at a time on
dst(to bound host memory) and nothing elsewhere. The collective runs eagerly, so every rank must call this before iterating.- Parameters:
state (dict)
dst (int)
- Return type:
Iterator[dict]
- is_available()#
Returns whether the distributed package is available.
- Return type:
bool
- is_distributed_launch()#
True if launched distributed (WORLD_SIZE>1), readable BEFORE the process group is set up.
Env-based, so parse-time code can detect the launch mode before
distributed_run()sets the process group up. Once it has,is_distributed_run()reads the live group.- Return type:
bool
- is_distributed_run()#
True if a real multi-rank job is running: the process group is initialized and world size > 1.
Runtime state (checks the live process group), unlike
is_distributed_launch()which reads only the launch env. A single process or a 1-rank group returns False. Backend-agnostic – true for any backend that initializedtorch.distributed(HF EP/DP loader, Megatron, plain DDP).- Return type:
bool
- is_fsdp2_model(model)#
Return True if any submodule of
modelhas been wrapped with FSDP2fully_shard.- Return type:
bool
- is_initialized()#
Returns whether the distributed package is initialized.
- Return type:
bool
- is_master(group=None)#
Returns whether the current process is the master process.
- Return type:
bool
- rank(group=None)#
Returns the rank of the current process.
- Return type:
int
- set_distribution_plan(model, plan)#
Record that
modelis distributed asplan, on it and on every submodule.Code handed a submodule, such as a VLM’s language model, then reads the same plan as code handed the whole model.
load_pretrainedrecords the plan it loads by; code that distributes a model itself records it here before calibrating or exporting the model.A model is distributed once, and its plan recorded once: recording a plan on a model that, or any of whose submodules, already records one raises, the same plan included, and leaves the model as it was. No code reloads or re-distributes a model it loaded.
- Parameters:
model (Module)
plan (DistributionPlan)
- Return type:
None
- shard_dataloader(loader, rank, world_size)#
Wrap a DataLoader so each rank sees a unique shard.
For map-style datasets a
DistributedSampleris used.drop_last=Falsekeeps per-rank batch counts equal (else a rank exits calibration early and hangs the others on the per-step collectives), at the cost of the sampler repeating up toworld_size - 1samples to pad the even split. For streamingIterableDatasetdatasets (which have nolen()and so can’t use a sampler) each rank strides its own deterministic copy of the stream via_StridedIterableShard. Every DataLoader worker must see that whole stream: a dataset that splits itself across workers byget_worker_info()would be split twice and lose samples, so load one withnum_workers=0.Forwards all non-sampler DataLoader settings from
loader(workers, pinning, prefetch, init fn, generator, …).- Parameters:
rank (int)
world_size (int)
- size(group=None)#
Returns the number of processes.
- Return type:
int