tensormesh.distributed.collectives 源代码

"""Distributed-safe utilities for shared random initialisation.

When TensorMesh runs across multiple processes (Gloo CPU or NCCL CUDA),
any value that influences the global problem state -- dataset
coefficients, sampled hyperparameters, initial guesses -- must be
identical on every rank. The natural failure mode is silent: each rank
calls the same constructor, samples from its own RNG, picks different
values, and the resulting distributed solve converges to a different
answer per rank with no error raised.

This module provides one helper:

* :func:`broadcast_from_rank0` -- call a factory on rank 0, broadcast
  the result to every other rank, return the shared tensor.

Library code that samples from RNG at construction time should route
through this helper so users do not need to litter their PDE setup
with manual ``torch.manual_seed`` / ``dist.broadcast`` plumbing. The
single-process path is a no-op (factory is called normally).

Usage::

    from tensormesh.distributed import broadcast_from_rank0

    class MyDataset:
        def __init__(self, K=8):
            self.a = broadcast_from_rank0(
                lambda: torch.empty((K, K)).uniform_(-1, 1)
            )
"""
from __future__ import annotations

from typing import Callable, Optional

import torch


[文档] def broadcast_from_rank0( factory: Callable[[], torch.Tensor], *, dst_device: Optional[torch.device] = None, ) -> torch.Tensor: """Sample on rank 0, broadcast to every rank. Parameters ---------- factory Zero-arg callable returning a fresh ``torch.Tensor``. Called only on rank 0 when a process group is active; called on every rank in single-process / no-process-group mode. dst_device Optional override for the device the broadcast lands on. Defaults to CUDA when NCCL is the active backend, CPU otherwise; the returned tensor is moved back to CPU so it composes with caller code that does its own ``.to(device)`` later. Pass an explicit device if you want a different placement. Returns ------- torch.Tensor A tensor with the same content on every rank. Single-process mode returns ``factory()`` directly. Notes ----- Uses :func:`torch.distributed.broadcast_object_list` under the hood, which pickles the tensor. This is fine for the typical use case (small coefficient matrices, scalar parameters); for large buffers prefer manual ``dist.broadcast`` with pre-allocated receive buffers on every rank. """ try: import torch.distributed as dist except ImportError: return factory() if not (dist.is_available() and dist.is_initialized()): return factory() world = dist.get_world_size() if world == 1: return factory() rank = dist.get_rank() if rank == 0: t = factory() payload = [t.detach().cpu()] else: payload = [None] dist.broadcast_object_list(payload, src=0) out: torch.Tensor = payload[0] if dst_device is not None: out = out.to(dst_device) return out