@@ -9506,7 +9506,11 @@ def dtensor_send(
95069506
95079507 resolved = strategy
95089508 if resolved == "auto":
9509- resolved = "materialize"
9509+ resolved = (
9510+ "optimal"
9511+ if dst_mesh is not None and dst_placements is not None
9512+ else "materialize"
9513+ )
95109514
95119515 if resolved == "materialize":
95129516 self._dtensor_send_materialize(dst, backend=backend)
@@ -9563,7 +9567,11 @@ def dtensor_recv(
95639567
95649568 resolved = strategy
95659569 if resolved == "auto":
9566- resolved = "materialize"
9570+ resolved = (
9571+ "optimal"
9572+ if src_mesh is not None and src_placements is not None
9573+ else "materialize"
9574+ )
95679575
95689576 if resolved == "materialize":
95699577 self._dtensor_recv_materialize(src, backend=backend)
@@ -9689,7 +9697,6 @@ def _dtensor_recv_redistribute(self, src, *, backend) -> None:
96899697 dtype = getattr(torch, meta["dtype"].replace("torch.", ""))
96909698 if meta["is_dtensor"]:
96919699 local_shape = torch.Size(meta["local_shape"])
9692- global_shape = torch.Size(meta["global_shape"])
96939700 buf = torch.empty(local_shape, dtype=dtype)
96949701 backend.recv_tensor(buf, src_int)
96959702 # Store as plain tensor with metadata attached.
@@ -9705,17 +9712,151 @@ def _dtensor_recv_redistribute(self, src, *, backend) -> None:
97059712 self._set_str(key, buf, inplace=False, validated=True)
97069713
97079714 def _dtensor_send_optimal(self, dst, *, backend, dst_mesh, dst_placements) -> None:
9708- raise NotImplementedError(
9709- "Strategy 'optimal' is not yet implemented. "
9710- "Use strategy='materialize' for now."
9715+ """Send using the optimal P2P transfer plan.
9716+
9717+ Computes which slices of each local shard need to go to which dst
9718+ rank, then issues targeted P2P sends for just those slices.
9719+ """
9720+ from torch import distributed as dist
9721+
9722+ from tensordict._dtensor import (
9723+ _compute_transfer_plan,
9724+ _mesh_all_ranks,
9725+ _mesh_to_rank_map,
97119726 )
97129727
9728+ my_rank = dist.get_rank()
9729+
9730+ # Normalise dst_placements to per-key dict
9731+ _dst_plc = dst_placements
9732+ if _dst_plc is not None and not isinstance(_dst_plc, dict):
9733+ _dst_plc = {key: _dst_plc for key in self.sorted_keys}
9734+
9735+ tag = 0
9736+ for key in self.sorted_keys:
9737+ value = self._get_str(key, NO_DEFAULT)
9738+ if _is_tensor_collection(type(value)):
9739+ raise NotImplementedError(
9740+ "Nested TensorDicts in dtensor_send are not yet supported."
9741+ )
9742+
9743+ if hasattr(value, "placements"):
9744+ src_mesh = value.device_mesh
9745+ src_placements_t = tuple(value.placements)
9746+ global_shape = value.shape
9747+ local_tensor = value.to_local()
9748+
9749+ key_dst_plc = _dst_plc[key] if _dst_plc is not None else None
9750+ if key_dst_plc is None:
9751+ raise ValueError(
9752+ f"dst_placements is required for optimal strategy, "
9753+ f"missing for key {key!r}."
9754+ )
9755+
9756+ src_mesh_shape = tuple(src_mesh.mesh.shape)
9757+ dst_mesh_shape = tuple(dst_mesh.mesh.shape)
9758+
9759+ src_rank_map = _mesh_to_rank_map(src_mesh)
9760+ dst_rank_map = _mesh_to_rank_map(dst_mesh)
9761+
9762+ plan = _compute_transfer_plan(
9763+ global_shape=global_shape,
9764+ src_mesh_shape=src_mesh_shape,
9765+ src_placements=src_placements_t,
9766+ dst_mesh_shape=dst_mesh_shape,
9767+ dst_placements=key_dst_plc,
9768+ src_rank_map=src_rank_map,
9769+ dst_rank_map=dst_rank_map,
9770+ )
9771+
9772+ for transfer in plan.sends_for_rank(my_rank):
9773+ chunk = local_tensor[transfer.src_slices].contiguous()
9774+ backend.send_tensor(chunk, transfer.dst_rank, tag=tag)
9775+ tag += 1
9776+ else:
9777+ # Non-DTensor: send to all dst ranks
9778+ for dst_rank in _mesh_all_ranks(dst_mesh):
9779+ backend.send_tensor(value.contiguous(), dst_rank, tag=tag)
9780+ tag += 1
9781+
97139782 def _dtensor_recv_optimal(self, src, *, backend, src_mesh, src_placements) -> None:
9714- raise NotImplementedError(
9715- "Strategy 'optimal' is not yet implemented. "
9716- "Use strategy='materialize' for now."
9783+ """Receive using the optimal P2P transfer plan.
9784+
9785+ Computes which slices this rank needs and from which src ranks,
9786+ then issues targeted P2P recvs and assembles the local shard.
9787+ """
9788+ from torch import distributed as dist
9789+
9790+ from tensordict._dtensor import (
9791+ _compute_transfer_plan,
9792+ _mesh_all_ranks,
9793+ _mesh_to_rank_map,
97179794 )
97189795
9796+ my_rank = dist.get_rank()
9797+
9798+ # Normalise src_placements to per-key dict
9799+ _src_plc = src_placements
9800+ if _src_plc is not None and not isinstance(_src_plc, dict):
9801+ _src_plc = {key: _src_plc for key in self.sorted_keys}
9802+
9803+ tag = 0
9804+ for key in self.sorted_keys:
9805+ value = self._get_str(key, NO_DEFAULT)
9806+ if _is_tensor_collection(type(value)):
9807+ raise NotImplementedError(
9808+ "Nested TensorDicts in dtensor_recv are not yet supported."
9809+ )
9810+
9811+ if hasattr(value, "placements"):
9812+ dst_mesh = value.device_mesh
9813+ dst_placements_t = tuple(value.placements)
9814+ global_shape = value.shape
9815+ local_tensor = value.to_local()
9816+
9817+ key_src_plc = _src_plc[key] if _src_plc is not None else None
9818+ if key_src_plc is None:
9819+ raise ValueError(
9820+ f"src_placements is required for optimal strategy, "
9821+ f"missing for key {key!r}."
9822+ )
9823+
9824+ src_mesh_shape = tuple(src_mesh.mesh.shape)
9825+ dst_mesh_shape = tuple(dst_mesh.mesh.shape)
9826+
9827+ src_rank_map = _mesh_to_rank_map(src_mesh)
9828+ dst_rank_map = _mesh_to_rank_map(dst_mesh)
9829+
9830+ plan = _compute_transfer_plan(
9831+ global_shape=global_shape,
9832+ src_mesh_shape=src_mesh_shape,
9833+ src_placements=key_src_plc,
9834+ dst_mesh_shape=dst_mesh_shape,
9835+ dst_placements=dst_placements_t,
9836+ src_rank_map=src_rank_map,
9837+ dst_rank_map=dst_rank_map,
9838+ )
9839+
9840+ for transfer in plan.recvs_for_rank(my_rank):
9841+ chunk_shape = tuple(
9842+ s.stop - s.start for s in transfer.global_slices
9843+ )
9844+ buf = torch.empty(chunk_shape, dtype=local_tensor.dtype)
9845+ backend.recv_tensor(buf, transfer.src_rank, tag=tag)
9846+ local_tensor[transfer.dst_slices] = buf
9847+ tag += 1
9848+
9849+ self._set_str(
9850+ key, local_tensor, inplace=True, validated=True
9851+ )
9852+ else:
9853+ # Non-DTensor: recv from the first src rank
9854+ first_src = _mesh_all_ranks(src_mesh)[0]
9855+ buf = torch.empty_like(value)
9856+ backend.recv_tensor(buf, first_src, tag=tag)
9857+ self._set_str(key, buf, inplace=False, validated=True)
9858+ tag += 1
9859+
97199860 def init_remote(
97209861 self,
97219862 dst: int | None = None,
0 commit comments