from __future__ import annotations
import os
from dataclasses import dataclass, field
from typing import Any, Callable
from modelexpress.refit.reshard.slice_plan import (
PullSegment,
_row_major_strides,
plan_pull,
)
from modelexpress.refit.reshard.transport import ReadDescriptor, Transport
from modelexpress.refit.reshard.types import (
CaptureResult,
RecordedCopy,
UnsupportedReshard,
)
@dataclass
class SourceInfo:
global_shape: tuple
dtype: Any
elsize: int
shards: list
@dataclass
class ConvertSource:
param_name: str
dest_shape: tuple
src_dtype: Any segments: list
@dataclass
class FullPullSource:
src_name: str
global_shape: tuple
dtype: Any
elsize: int
segments: list
copies: list
@dataclass
class TransferPlan:
segments: list = field(default_factory=list)
fallback: list = field(default_factory=list)
converts: list = field(default_factory=list) full_pulls: list = field(default_factory=list) unbounded_sources: list = field(default_factory=list)
exact_descriptor_count: int = 0
exact_bytes: int = 0
def _all_segments(self):
yield from self.segments
for convert in self.converts:
yield from convert.segments
for full_pull in self.full_pulls:
yield from full_pull.segments
def bytes_planned(self) -> int:
return sum(segment.nbytes for segment in self._all_segments())
def descriptor_count(self) -> int:
return sum(1 for _ in self._all_segments())
def sessions(self) -> set:
return {segment.session for segment in self._all_segments()}
def descriptor_savings(self) -> int:
return max(0, self.exact_descriptor_count - self.descriptor_count())
def extra_wire_bytes(self) -> int:
return max(0, self.bytes_planned() - self.exact_bytes)
def _full_pull_segments(src_name: str, source: SourceInfo) -> list:
if not source.global_shape:
raise UnsupportedReshard(f"{src_name}: scalar full-pull is unsupported")
trailing = 1
for extent in source.global_shape[1:]:
trailing *= int(extent)
ordered = sorted(source.shards, key=lambda shard: int(shard.shard_offset[0]))
segments = []
next_row = 0
for shard in ordered:
if len(shard.shape) != len(source.global_shape) or len(
shard.shard_offset
) != len(source.global_shape):
raise UnsupportedReshard(
f"{src_name}: shard rank does not match global rank"
)
if tuple(shard.shape[1:]) != tuple(source.global_shape[1:]) or any(
int(offset) != 0 for offset in shard.shard_offset[1:]
):
raise UnsupportedReshard(
f"{src_name}: bounded full-pull requires dim-0 shards, got "
f"offset={shard.shard_offset} shape={shard.shape}"
)
start = int(shard.shard_offset[0])
rows = int(shard.shape[0])
if start != next_row:
raise UnsupportedReshard(
f"{src_name}: dim-0 shards have a gap or overlap at row "
f"{next_row} (next shard starts at {start})"
)
elements = rows * trailing
segments.append(
PullSegment(
session=shard.session,
src_addr=shard.addr,
param_name=src_name,
dst_byte=start * trailing * source.elsize,
nbytes=elements * source.elsize,
)
)
next_row += rows
if next_row != int(source.global_shape[0]):
raise UnsupportedReshard(
f"{src_name}: dim-0 shards cover {next_row} of "
f"{source.global_shape[0]} rows"
)
return segments
def plan_transfer(
capture: CaptureResult,
sources: dict,
*,
max_segments_per_copy: int | None = None,
) -> TransferPlan:
plan = TransferPlan()
fallback_seen: set = set()
exact_by_source: dict[str, list[tuple[RecordedCopy, list]]] = {}
full_pull_names: set[str] = set()
if max_segments_per_copy is None:
max_segments_per_copy = int(
os.environ.get("MX_RESHARD_MAX_SEGMENTS_PER_COPY", "64")
)
if max_segments_per_copy < 1:
raise ValueError("max_segments_per_copy must be at least 1")
def mark_fallback(name: str) -> None:
if name not in fallback_seen:
fallback_seen.add(name)
plan.fallback.append(name)
for name in capture.unsupported:
mark_fallback(name)
for copy in capture.copies:
src = sources.get(copy.src_name)
if src is None:
mark_fallback(copy.src_name)
continue
if src.dtype != copy.dest_dtype:
staging_copy = RecordedCopy(
src_name=copy.src_name,
op_chain=copy.op_chain,
param_name=copy.param_name,
dest_offset=0,
dest_shape=copy.dest_shape,
dest_stride=_row_major_strides(copy.dest_shape),
dest_dtype=src.dtype,
)
try:
segments = plan_pull(
staging_copy, src.global_shape, src.dtype, src.elsize, src.shards
)
except UnsupportedReshard:
mark_fallback(copy.src_name)
continue
plan.exact_descriptor_count += len(segments)
plan.exact_bytes += sum(segment.nbytes for segment in segments)
plan.converts.append(
ConvertSource(
copy.param_name, tuple(copy.dest_shape), src.dtype, segments
)
)
continue
try:
segments = plan_pull(
copy, src.global_shape, src.dtype, src.elsize, src.shards
)
except UnsupportedReshard:
mark_fallback(copy.src_name)
continue
plan.exact_descriptor_count += len(segments)
plan.exact_bytes += sum(segment.nbytes for segment in segments)
exact_by_source.setdefault(copy.src_name, []).append((copy, segments))
if len(segments) > max_segments_per_copy:
full_pull_names.add(copy.src_name)
for src_name, entries in exact_by_source.items():
if src_name not in full_pull_names:
for _copy, segments in entries:
plan.segments.extend(segments)
continue
source = sources[src_name]
try:
full_segments = _full_pull_segments(src_name, source)
except UnsupportedReshard:
plan.unbounded_sources.append(src_name)
for _copy, segments in entries:
plan.segments.extend(segments)
continue
plan.full_pulls.append(
FullPullSource(
src_name=src_name,
global_shape=tuple(source.global_shape),
dtype=source.dtype,
elsize=source.elsize,
segments=full_segments,
copies=[copy for copy, _segments in entries],
)
)
return plan
def exact_descriptors(
plan: TransferPlan,
resolve_param_ptr: Callable[[str], int],
) -> list[ReadDescriptor]:
return [
ReadDescriptor(
session=seg.session,
src_addr=seg.src_addr,
dst_addr=resolve_param_ptr(seg.param_name) + seg.dst_byte,
nbytes=seg.nbytes,
)
for seg in plan.segments
]
def execute_transfer(
plan: TransferPlan,
resolve_param_ptr: Callable[[str], int],
transport: Transport,
) -> dict:
descriptors = exact_descriptors(plan, resolve_param_ptr)
transport.read(descriptors)
return {
"segments": len(descriptors),
"bytes": sum(d.nbytes for d in descriptors),
"fallback": list(plan.fallback),
}