distributed#

Utility functions for using torch.distributed.

Classes

DistributedProcessGroup

A convenient wrapper around torch.distributed.ProcessGroup objects.

DistributedState

This process's place in a run: its rank, the run's size, and the device it computes on.

DistributionPlan

How a model is distributed over the ranks of a run.

ParallelState

A class to manage various parallel groups such as data parallel, tensor parallel etc.

Functions

all_reduce

Return combine of every rank's fn(), on every rank.

backend

Returns the distributed backend.

barrier

Synchronizes all processes.

broadcast_from

Return fn() run on rank only, on every rank.

distributed_run

Own the run's process group for the block, and yield this process's state.

distribution_plan

How model is distributed: the plan recorded on it (set_distribution_plan()).

fsdp2_wrap

FSDP2-shard a HF causal LM's decoder layers, and the root unless shard_root=False.

gather_bytes

Gathers byte strings to global rank dst only, without unpickling anything on the way.

gather_state_dicts

Gathers state from every rank of group to global rank dst, loaded with weights_only.

is_available

Returns whether the distributed package is available.

is_distributed_launch

True if launched distributed (WORLD_SIZE>1), readable BEFORE the process group is set up.

is_distributed_run

True if a real multi-rank job is running: the process group is initialized and world size > 1.

is_fsdp2_model

Return True if any submodule of model has been wrapped with FSDP2 fully_shard.

is_initialized

Returns whether the distributed package is initialized.

is_master

Returns whether the current process is the master process.

rank

Returns the rank of the current process.

set_distribution_plan

Record that model is distributed as plan, on it and on every submodule.

size

Returns the number of processes.

class DistributedProcessGroup#

Bases: object

A 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:
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: object

This 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: object

How a model is distributed over the ranks of a run.

  • world_size – the ranks that hold the model; 1 for a single process.

  • ep_size – the expert-parallel degree: an MoE’s routed experts are sharded over groups of ep_size ranks. 1 shards 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_size times.

__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: object

A class to manage various parallel groups such as data parallel, tensor parallel etc.

Specify the parallel groups of type torch.distributed.ProcessGroup for 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 combine of every rank’s fn(), on every rank.

combine takes the ranks’ values in rank order and returns the one every rank gets, e.g. min for a batch size each rank’s memory bounds, where one rank’s choice could run another out of memory. If fn raises 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 run fn runs 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 on rank only, 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 fn raises, every rank raises, instead of waiting in a broadcast rank never sends. Ranks broadcasting different labels at the same point fail. Collective: every rank calls it at the same point, with the same rank. Outside a distributed run fn runs alone.

Parameters:
  • label (str)

  • fn (Callable[[], _V])

  • rank (int)

Return type:

_V

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, with timeout. It yields this rank’s DistributedState, on that CUDA device. A single process sets nothing up and is rank 0 of a run of one, on device.

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 model is 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:

DistributionPlan

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 dst only, without unpickling anything on the way.

Unlike torch.distributed.gather_object it never runs pickle, and it moves chunk_bytes per rank at a time to bound device memory. Returns the per-rank bytes on dst and None elsewhere.

Parameters:
  • data (bytes)

  • dst (int)

  • chunk_bytes (int)

Return type:

list[bytearray] | None

gather_state_dicts(state, dst, group=None)#

Gathers state from every rank of group to global rank dst, loaded with weights_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 initialized torch.distributed (HF EP/DP loader, Megatron, plain DDP).

Return type:

bool

is_fsdp2_model(model)#

Return True if any submodule of model has been wrapped with FSDP2 fully_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 model is distributed as plan, 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_pretrained records 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:
Return type:

None

size(group=None)#

Returns the number of processes.

Return type:

int