Files
wehub-resource-sync b5ecf06f65
Code Format Check / format-check (push) Failing after 1s
chore: import upstream snapshot with attribution
2026-07-13 12:24:32 +08:00

84 lines
2.5 KiB
Python

import os
import time
import torch
import torch.distributed as dist
# noinspection PyUnresolvedReferences
import deep_ep._C as _C
class NCCLCommHandle:
"""
A wrapper around a raw NCCL communicator. Manages the lifecycle of the communicator if created by DeepEP,
or simply wraps an existing one if obtained from PyTorch.
Attributes:
nccl_comm: the raw NCCL communicator.
managed: whether the communicator was created by DeepEP and should be destroyed when this handle is dropped.
"""
def __init__(self, nccl_comm: int, managed: bool):
self.nccl_comm = nccl_comm
self.managed = managed
self.destroy = _C.destroy_nccl_comm
def __del__(self):
if self.managed:
self.destroy(self.nccl_comm)
def get(self) -> int:
"""
Get the raw NCCL communicator.
Returns:
nccl_comm: the raw NCCL communicator.
"""
return self.nccl_comm
_storage = dict()
def get_nccl_comm_handle(group: dist.ProcessGroup, force_new_comm: bool = False) -> NCCLCommHandle:
"""
Get or create an NCCL communicator handle for the given process group.
Results are cached, so subsequent calls with the same group return the same handle.
Arguments:
group: the communication group.
force_new_comm: if set, never reuse PyTorch's communicator and never hit the cache; always
create a fresh DeepEP-managed comm.
Returns:
handle: the NCCL communicator handle.
"""
# Check cache hit
global _storage
if not force_new_comm and group in _storage:
return _storage[group]
# New PyTorch has such API
backend = group._get_backend(torch.device('cuda'))
if not force_new_comm and hasattr(backend, '_comm_ptr') and int(os.getenv('EP_REUSE_NCCL_COMM', '1')):
_storage[group] = NCCLCommHandle(backend._comm_ptr(), False)
return _storage[group]
# For old PyTorch, we have to recreate a NCCL comm
nccl_unique_ids = [None, ] * group.size()
dist.all_gather_object(nccl_unique_ids, _C.get_local_nccl_unique_id(), group)
root_unique_id = nccl_unique_ids[0]
# Create a new communicator
key = time.time_ns() if force_new_comm else group
_storage[key] = NCCLCommHandle(
_C.create_nccl_comm(root_unique_id, group.size(), group.rank()), True)
return _storage[key]
def destroy_all_managed_nccl_comm() -> None:
"""
Destroy all cached NCCL communicator handles and clear the cache.
"""
_storage.clear()