# Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: Apache-2.0 # DeepSpeed Team import deepspeed from deepspeed import utils from packaging import version import inspect from .utils import * from .backend import * from .comm import * from ..runtime import compiler from deepspeed.utils.torch import required_torch_version import os DS_COMM_ALL_GATHER_OFF = False DS_COMM_REDUCE_SCATTER_OFF = False DS_COMM_BROADCAST_OFF = False DS_COMM_ALL_REDUCE_OFF = False DS_COMM_REDUCE_OFF = False def disable_compiler_collective(func): if required_torch_version(min_version=2.3): return func return compiler.disable(func) def build_shm_op(): builder = get_accelerator().create_op_builder("ShareMemCommBuilder") if builder is None or not deepspeed.ops.__compatible_ops__.get(builder.NAME, False): return None shm_cpp_module = builder.load() print(f'DeepSpeed {builder.absolute_name()} built successfully') return shm_cpp_module def has_coalescing_manager(): has_c10d = hasattr(torch.distributed, 'distributed_c10d') return has_c10d and hasattr(torch.distributed.distributed_c10d, '_coalescing_manager') def has_all_reduce_coalesced(): return hasattr(torch.distributed, "all_reduce_coalesced") and required_torch_version(min_version=1.13) def get_coalescing_manager(group, device, reqs, async_op): if required_torch_version(min_version=2.0, max_version=2.0): return torch.distributed.distributed_c10d._coalescing_manager(group, device=device, reqs=reqs) elif required_torch_version(min_version=2.1): return torch.distributed.distributed_c10d._coalescing_manager(group, device=device, async_ops=async_op) else: return torch.distributed.distributed_c10d._coalescing_manager(group, reqs) ##Utilities to turn comm off ##TODO: move to base comm (wrapper) def all_gather_comm_off(flag=False): global DS_COMM_ALL_GATHER_OFF DS_COMM_ALL_GATHER_OFF = flag def reduce_scatter_comm_off(flag=False): global DS_COMM_REDUCE_SCATTER_OFF DS_COMM_REDUCE_SCATTER_OFF = flag def broadcast_comm_off(flag=False): global DS_COMM_BROADCAST_OFF DS_COMM_BROADCAST_OFF = flag def all_reduce_comm_off(flag=False): global DS_COMM_ALL_REDUCE_OFF DS_COMM_ALL_REDUCE_OFF = flag def reduce_comm_off(flag=False): global DS_COMM_REDUCE_OFF DS_COMM_REDUCE_OFF = flag #assumption: all_gather and reduce scatter ## are what we care about def backward_comm_off(flag=False): all_gather_comm_off(flag) reduce_scatter_comm_off(flag) class Noop: def wait(self): return None class TorchBackend(Backend): """ A light-weight wrapper class for torch.distributed API. Only a subset of functions are wrapped. Once the init_process_group is initialized, standard torch.distributed.* can be used directly so no need to wrap all the functions. We can keep adding wrappers as needed. """ def __init__(self, backend, timeout, init_method, rank=-1, world_size=-1, name='torch'): super(TorchBackend, self).__init__() self.shm_comm_op = build_shm_op() self.has_all_reduce_coalesced = has_all_reduce_coalesced() self.has_coalescing_manager = has_coalescing_manager() self.all_gather_function = self.get_all_gather_function() self.reduce_scatter_function = self.get_reduce_scatter_function() self.initialized = True self.name = name # Future functionality to support ds.initialize() on a single GPU # The idea is to fake that dist backend is initialized even when # it is not so we can run on a single GPU without doing any init_process_group self.single_gpu_mode = True self.init_process_group(backend, timeout, init_method, rank, world_size) if self.shm_comm_op != None: self.shm_comm_op.initialize(self.get_world_size(), self.get_rank()) # Best-effort SDMA (mori) backend acquisition. Stays None on # non-AMD/ROCm or when mori is unavailable; in that case # all_gather_into_tensor below transparently falls through to # torch.distributed.all_gather_into_tensor. self._init_sdma_backend() def _init_sdma_backend(self): """Try to enable the mori SDMA path for ``all_gather_into_tensor``. Failure (non-AMD, mori not installed, shmem init error) leaves the handle unset and the standard torch.distributed allgather is used. """ try: from . import mori as _mori _mori.init() except Exception: pass @classmethod @disable_compiler_collective def get_all_gather_function(self): if hasattr(torch.distributed, "all_gather_into_tensor"): return torch.distributed.all_gather_into_tensor elif hasattr(torch.distributed, "_all_gather_base"): return torch.distributed._all_gather_base return None @classmethod @disable_compiler_collective def get_reduce_scatter_function(self): if hasattr(torch.distributed, "reduce_scatter_tensor"): return torch.distributed.reduce_scatter_tensor elif hasattr(torch.distributed, "_reduce_scatter_base"): return torch.distributed._reduce_scatter_base return None def has_all_gather_into_tensor(self): return self.all_gather_function is not None def has_reduce_scatter_tensor(self): return self.reduce_scatter_function is not None def init_process_group(self, backend, timeout, init_method, rank, world_size): if not torch.distributed.is_initialized(): kwargs = dict(timeout=timeout, init_method=init_method, rank=rank, world_size=world_size) # 1. device_id arg was added in torch==2.3 # 2. setting device_id leads to hanging in 2.6.0