204 lines
6.7 KiB
Python
204 lines
6.7 KiB
Python
from dataclasses import dataclass
|
|
from typing import TYPE_CHECKING, List, Optional
|
|
|
|
import ray
|
|
from ray.experimental.rdt.tensor_transport_manager import (
|
|
CommunicatorMetadata,
|
|
TensorTransportManager,
|
|
TensorTransportMetadata,
|
|
)
|
|
|
|
if TYPE_CHECKING:
|
|
import torch
|
|
|
|
|
|
@dataclass
|
|
class CollectiveTransportMetadata(TensorTransportMetadata):
|
|
"""Metadata for tensors stored in the GPU object store for collective transport."""
|
|
|
|
|
|
@dataclass
|
|
class CollectiveCommunicatorMetadata(CommunicatorMetadata):
|
|
"""Metadata for the collective communicator (e.g. NCCL, GLOO).
|
|
|
|
Args:
|
|
src_rank: The rank of the source actor.
|
|
dst_rank: The rank of the destination actor.
|
|
"""
|
|
|
|
communicator_name: str = ""
|
|
src_rank: Optional[int] = None
|
|
dst_rank: Optional[int] = None
|
|
|
|
|
|
class CollectiveTensorTransport(TensorTransportManager):
|
|
def tensor_transport_backend(self) -> str:
|
|
raise NotImplementedError(
|
|
"NCCLTensorTransport or GLOOTensorTransport should be used instead of this base class."
|
|
)
|
|
|
|
@staticmethod
|
|
def is_one_sided() -> bool:
|
|
return False
|
|
|
|
@staticmethod
|
|
def can_abort_transport() -> bool:
|
|
return False
|
|
|
|
def actor_has_tensor_transport(self, actor: "ray.actor.ActorHandle") -> bool:
|
|
from ray.experimental.collective import get_collective_groups
|
|
|
|
communicators = get_collective_groups(
|
|
[actor], backend=self.tensor_transport_backend()
|
|
)
|
|
return len(communicators) > 0
|
|
|
|
def extract_tensor_transport_metadata(
|
|
self,
|
|
obj_id: str,
|
|
rdt_object: List["torch.Tensor"],
|
|
) -> CollectiveTransportMetadata:
|
|
tensor_meta = []
|
|
device = None
|
|
if rdt_object:
|
|
device = rdt_object[0].device
|
|
for t in rdt_object:
|
|
if t.device.type != device.type:
|
|
raise ValueError(
|
|
"All tensors in an RDT object must have the same device type."
|
|
)
|
|
tensor_meta.append((t.shape, t.dtype))
|
|
return CollectiveTransportMetadata(
|
|
tensor_meta=tensor_meta,
|
|
tensor_device=device.type if device else None,
|
|
)
|
|
|
|
def get_communicator_metadata(
|
|
self,
|
|
src_actor: "ray.actor.ActorHandle",
|
|
dst_actor: "ray.actor.ActorHandle",
|
|
backend: Optional[str] = None,
|
|
) -> CollectiveCommunicatorMetadata:
|
|
|
|
from ray.experimental.collective import get_collective_groups
|
|
|
|
communicators = get_collective_groups(
|
|
[src_actor, dst_actor],
|
|
backend=backend,
|
|
)
|
|
# TODO(kevin85421): Support multiple communicators.
|
|
if len(communicators) == 0:
|
|
raise ValueError(
|
|
f"No communicators found for actors {src_actor} and {dst_actor}. "
|
|
"Create a communicator with "
|
|
"`ray.experimental.collective.create_collective_group` "
|
|
"before calling actor tasks. with non-default tensor_transport."
|
|
)
|
|
elif len(communicators) > 1:
|
|
raise ValueError(
|
|
f"There are {len(communicators)} possible communicators that contain actors {src_actor} and {dst_actor}. "
|
|
"Currently, RDT objects only support one communicator. Please make sure only "
|
|
"one communicator exists."
|
|
)
|
|
communicator = communicators[0]
|
|
src_rank = communicator.get_rank(src_actor)
|
|
if src_rank == -1:
|
|
raise ValueError(
|
|
f"Sender actor {src_actor} not found in communicator. "
|
|
"Please make sure the sender and receiver are in the same communicator."
|
|
)
|
|
dst_rank = communicator.get_rank(dst_actor)
|
|
if dst_rank == -1:
|
|
raise ValueError(
|
|
f"Receiver actor {dst_actor} not found in communicator. "
|
|
"Please make sure the sender and receiver are in the same communicator."
|
|
)
|
|
|
|
communicator_metadata = CollectiveCommunicatorMetadata(
|
|
communicator_name=communicator.name,
|
|
src_rank=src_rank,
|
|
dst_rank=dst_rank,
|
|
)
|
|
return communicator_metadata
|
|
|
|
def recv_multiple_tensors(
|
|
self,
|
|
obj_id: str,
|
|
tensor_transport_metadata: TensorTransportMetadata,
|
|
communicator_metadata: CommunicatorMetadata,
|
|
target_buffers: Optional[List["torch.Tensor"]] = None,
|
|
):
|
|
from ray.experimental.rdt.util import (
|
|
create_empty_tensors_from_metadata,
|
|
)
|
|
from ray.util.collective.collective import recv
|
|
|
|
assert isinstance(tensor_transport_metadata, CollectiveTransportMetadata)
|
|
assert isinstance(communicator_metadata, CollectiveCommunicatorMetadata)
|
|
|
|
tensors = target_buffers or create_empty_tensors_from_metadata(
|
|
tensor_transport_metadata
|
|
)
|
|
for tensor in tensors:
|
|
recv(
|
|
tensor,
|
|
communicator_metadata.src_rank,
|
|
communicator_metadata.communicator_name,
|
|
)
|
|
return tensors
|
|
|
|
def send_multiple_tensors(
|
|
self,
|
|
tensors: List["torch.Tensor"],
|
|
tensor_transport_metadata: TensorTransportMetadata,
|
|
communicator_metadata: CommunicatorMetadata,
|
|
):
|
|
import ray.util.collective as collective
|
|
|
|
assert isinstance(
|
|
tensor_transport_metadata, CollectiveTransportMetadata
|
|
), "metadata must be a CollectiveTransportMetadata object for non-NIXL transport"
|
|
assert isinstance(
|
|
communicator_metadata, CollectiveCommunicatorMetadata
|
|
), "metadata must be a CollectiveCommunicatorMetadata object for non-NIXL transport"
|
|
|
|
device = tensors[0].device if tensors else None
|
|
|
|
for tensor in tensors:
|
|
if tensor.device.type != device.type:
|
|
raise ValueError(
|
|
f"tensor device {tensor.device} does not match device {device}"
|
|
)
|
|
collective.send(
|
|
tensor,
|
|
communicator_metadata.dst_rank,
|
|
communicator_metadata.communicator_name,
|
|
)
|
|
|
|
def garbage_collect(
|
|
self,
|
|
obj_id: str,
|
|
tensor_transport_meta: TensorTransportMetadata,
|
|
tensors: List["torch.Tensor"],
|
|
):
|
|
pass
|
|
|
|
def abort_transport(
|
|
self,
|
|
obj_id: str,
|
|
communicator_metadata: CommunicatorMetadata,
|
|
):
|
|
raise NotImplementedError(
|
|
"Collective transport does not support abort_transport for now."
|
|
)
|
|
|
|
|
|
class NCCLTensorTransport(CollectiveTensorTransport):
|
|
def tensor_transport_backend(self) -> str:
|
|
return "NCCL"
|
|
|
|
|
|
class GLOOTensorTransport(CollectiveTensorTransport):
|
|
def tensor_transport_backend(self) -> str:
|
|
return "GLOO"
|