chore: import upstream snapshot with attribution
PR Test AMD / cancel-on-close (push) Has been skipped
PR Test NVIDIA ARM / scan (push) Has been skipped
PR Test NVIDIA / cancel-on-close (push) Has been skipped
PR Test AMD / scan (push) Has been skipped
PR Test NVIDIA ARM / cancel-on-close (push) Has been skipped
PR Test NVIDIA / scan (push) Has been skipped
Release Docker Images / build (cu129-torch-2.11.0) (push) Has been skipped
Release Docker Images / build (cu130-torch-2.11.0) (push) Has been skipped
Release PyPI / publish (push) Has been skipped
Scheduler Python Test / test (push) Successful in 27m19s
Docs / build (push) Successful in 28m8s
Scheduler C++ Test / test (push) Successful in 28m19s
Scheduler C++ Test / test-flat (push) Successful in 28m18s
Docs / deploy (push) Has been cancelled
PR Test AMD / finish (push) Has been cancelled
PR Test NVIDIA / finish (push) Has been cancelled
PR Test NVIDIA ARM / finish (push) Has been cancelled
PR Test NVIDIA ARM / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
PR Test AMD / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
PR Test NVIDIA / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
PR Test AMD / cancel-on-close (push) Has been skipped
PR Test NVIDIA ARM / scan (push) Has been skipped
PR Test NVIDIA / cancel-on-close (push) Has been skipped
PR Test AMD / scan (push) Has been skipped
PR Test NVIDIA ARM / cancel-on-close (push) Has been skipped
PR Test NVIDIA / scan (push) Has been skipped
Release Docker Images / build (cu129-torch-2.11.0) (push) Has been skipped
Release Docker Images / build (cu130-torch-2.11.0) (push) Has been skipped
Release PyPI / publish (push) Has been skipped
Scheduler Python Test / test (push) Successful in 27m19s
Docs / build (push) Successful in 28m8s
Scheduler C++ Test / test (push) Successful in 28m19s
Scheduler C++ Test / test-flat (push) Successful in 28m18s
Docs / deploy (push) Has been cancelled
PR Test AMD / finish (push) Has been cancelled
PR Test NVIDIA / finish (push) Has been cancelled
PR Test NVIDIA ARM / finish (push) Has been cancelled
PR Test NVIDIA ARM / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
PR Test AMD / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
PR Test NVIDIA / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,392 @@
|
||||
# Copyright (c) 2026 LightSeek Foundation
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
import math
|
||||
from functools import cached_property
|
||||
|
||||
Group = tuple[int, ...]
|
||||
|
||||
|
||||
def _resolve_parallelism_sizes(world_size: int, *sizes: int | None) -> tuple[int, ...]:
|
||||
"""Resolve the parallelism sizes given world_size.
|
||||
|
||||
`sizes` is ordered innermost (fastest-varying) to outermost.
|
||||
"""
|
||||
assert all(x is None or x > 0 for x in sizes)
|
||||
|
||||
resolved = [x for x in sizes]
|
||||
num_to_resolve = sum(x is None for x in sizes)
|
||||
if num_to_resolve > 0:
|
||||
provided_size = math.prod(x for x in sizes if x is not None)
|
||||
assert provided_size <= world_size
|
||||
assert world_size % provided_size == 0
|
||||
resolved_size = world_size // provided_size
|
||||
|
||||
for index, size in enumerate(resolved):
|
||||
if size is None:
|
||||
resolved[index] = resolved_size
|
||||
resolved_size = 1
|
||||
|
||||
assert math.prod(resolved) == world_size
|
||||
return tuple(resolved)
|
||||
|
||||
|
||||
def _make_parallelism_rank(rank: int, size: int, stride: int = 1) -> int:
|
||||
"""Return the rank of given size and stride."""
|
||||
return (rank // stride) % size
|
||||
|
||||
|
||||
def _make_parallelism_group(rank: int, size: int, stride: int = 1) -> Group:
|
||||
"""Return the group of ranks of given size and stride."""
|
||||
base = rank - (rank // stride % size) * stride
|
||||
return tuple(base + j * stride for j in range(size))
|
||||
|
||||
|
||||
class MappingBase:
|
||||
|
||||
def __init__(self, rank: int | None = None, world_size: int = 1):
|
||||
assert rank is None or rank >= 0
|
||||
self._rank = rank
|
||||
assert world_size > 0
|
||||
self._world_size = world_size
|
||||
|
||||
@property
|
||||
def rank(self) -> int:
|
||||
assert self._rank is not None, "rank is not initialized"
|
||||
return self._rank
|
||||
|
||||
@rank.setter
|
||||
def rank(self, rank: int):
|
||||
assert self._rank is None, "rank is already initialized"
|
||||
assert rank >= 0
|
||||
self._rank = rank
|
||||
self._on_rank_initialized(rank)
|
||||
|
||||
def _on_rank_initialized(self, rank: int):
|
||||
return None
|
||||
|
||||
@property
|
||||
def world_size(self) -> int:
|
||||
return self._world_size
|
||||
|
||||
@cached_property
|
||||
def world_group(self) -> Group:
|
||||
return _make_parallelism_group(self.rank, self.world_size, stride=1)
|
||||
|
||||
|
||||
class DenseLayerMapping(MappingBase):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
rank: int | None = None,
|
||||
world_size: int = 1,
|
||||
tp_size: int | None = None,
|
||||
dp_size: int | None = None,
|
||||
):
|
||||
super().__init__(rank, world_size)
|
||||
self.tp_size, self.dp_size = _resolve_parallelism_sizes(
|
||||
self.world_size, tp_size, dp_size
|
||||
)
|
||||
|
||||
@cached_property
|
||||
def has_tp(self) -> bool:
|
||||
return self.tp_size > 1
|
||||
|
||||
@cached_property
|
||||
def tp_rank(self) -> int:
|
||||
return _make_parallelism_rank(self.rank, self.tp_size, stride=1)
|
||||
|
||||
@cached_property
|
||||
def tp_group(self) -> Group:
|
||||
return _make_parallelism_group(self.rank, self.tp_size, stride=1)
|
||||
|
||||
@cached_property
|
||||
def has_dp(self) -> bool:
|
||||
return self.dp_size > 1
|
||||
|
||||
@cached_property
|
||||
def dp_rank(self) -> int:
|
||||
return _make_parallelism_rank(self.rank, self.dp_size, stride=self.tp_size)
|
||||
|
||||
@cached_property
|
||||
def dp_group(self) -> Group:
|
||||
return _make_parallelism_group(self.rank, self.dp_size, stride=self.tp_size)
|
||||
|
||||
|
||||
class AttentionLayerMapping(MappingBase):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
rank: int | None = None,
|
||||
world_size: int = 1,
|
||||
tp_size: int | None = None,
|
||||
cp_size: int | None = None,
|
||||
dp_size: int | None = None,
|
||||
):
|
||||
super().__init__(rank, world_size)
|
||||
self.tp_size, self.cp_size, self.dp_size = _resolve_parallelism_sizes(
|
||||
self.world_size, tp_size, cp_size, dp_size
|
||||
)
|
||||
|
||||
@cached_property
|
||||
def has_tp(self) -> bool:
|
||||
return self.tp_size > 1
|
||||
|
||||
@cached_property
|
||||
def tp_rank(self) -> int:
|
||||
return _make_parallelism_rank(self.rank, self.tp_size, stride=1)
|
||||
|
||||
@cached_property
|
||||
def tp_group(self) -> Group:
|
||||
return _make_parallelism_group(self.rank, self.tp_size, stride=1)
|
||||
|
||||
@cached_property
|
||||
def has_cp(self) -> bool:
|
||||
return self.cp_size > 1
|
||||
|
||||
@cached_property
|
||||
def cp_rank(self) -> int:
|
||||
return _make_parallelism_rank(self.rank, self.cp_size, stride=self.tp_size)
|
||||
|
||||
@cached_property
|
||||
def cp_group(self) -> Group:
|
||||
return _make_parallelism_group(self.rank, self.cp_size, stride=self.tp_size)
|
||||
|
||||
@cached_property
|
||||
def has_dp(self) -> bool:
|
||||
return self.dp_size > 1
|
||||
|
||||
@cached_property
|
||||
def dp_rank(self) -> int:
|
||||
return _make_parallelism_rank(
|
||||
self.rank, self.dp_size, stride=self.tp_size * self.cp_size
|
||||
)
|
||||
|
||||
@cached_property
|
||||
def dp_group(self) -> Group:
|
||||
return _make_parallelism_group(
|
||||
self.rank, self.dp_size, stride=self.tp_size * self.cp_size
|
||||
)
|
||||
|
||||
def scatter_index(self, rank: int) -> int:
|
||||
"""Index of ``rank`` in a dp-major/tp-minor scattered token count
|
||||
table; cp peers share their dp group's tp split."""
|
||||
tp_rank = _make_parallelism_rank(rank, self.tp_size, stride=1)
|
||||
dp_rank = _make_parallelism_rank(
|
||||
rank, self.dp_size, stride=self.tp_size * self.cp_size
|
||||
)
|
||||
return dp_rank * self.tp_size + tp_rank
|
||||
|
||||
|
||||
class MoeLayerMapping(MappingBase):
|
||||
def __init__(
|
||||
self,
|
||||
rank: int | None = None,
|
||||
world_size: int = 1,
|
||||
tp_size: int | None = None,
|
||||
ep_size: int | None = None,
|
||||
dp_size: int | None = None,
|
||||
):
|
||||
super().__init__(rank, world_size)
|
||||
self.tp_size, self.ep_size, self.dp_size = _resolve_parallelism_sizes(
|
||||
self.world_size, tp_size, ep_size, dp_size
|
||||
)
|
||||
|
||||
@cached_property
|
||||
def has_tp(self) -> bool:
|
||||
return self.tp_size > 1
|
||||
|
||||
@cached_property
|
||||
def tp_rank(self) -> int:
|
||||
return _make_parallelism_rank(self.rank, self.tp_size, stride=1)
|
||||
|
||||
@cached_property
|
||||
def tp_group(self) -> Group:
|
||||
return _make_parallelism_group(self.rank, self.tp_size, stride=1)
|
||||
|
||||
@cached_property
|
||||
def has_ep(self) -> bool:
|
||||
return self.ep_size > 1
|
||||
|
||||
@cached_property
|
||||
def ep_rank(self) -> int:
|
||||
return _make_parallelism_rank(self.rank, self.ep_size, stride=self.tp_size)
|
||||
|
||||
@cached_property
|
||||
def ep_group(self) -> Group:
|
||||
return _make_parallelism_group(self.rank, self.ep_size, stride=self.tp_size)
|
||||
|
||||
@cached_property
|
||||
def has_tp_ep(self) -> bool:
|
||||
return self.tp_ep_size > 1
|
||||
|
||||
@cached_property
|
||||
def tp_ep_size(self) -> int:
|
||||
return self.tp_size * self.ep_size
|
||||
|
||||
@cached_property
|
||||
def tp_ep_rank(self) -> int:
|
||||
return _make_parallelism_rank(self.rank, self.tp_ep_size, stride=1)
|
||||
|
||||
@cached_property
|
||||
def tp_ep_group(self) -> Group:
|
||||
return _make_parallelism_group(self.rank, self.tp_ep_size, stride=1)
|
||||
|
||||
@cached_property
|
||||
def has_dp(self) -> bool:
|
||||
return self.dp_size > 1
|
||||
|
||||
@cached_property
|
||||
def dp_rank(self) -> int:
|
||||
return _make_parallelism_rank(
|
||||
self.rank, self.dp_size, stride=self.tp_size * self.ep_size
|
||||
)
|
||||
|
||||
@cached_property
|
||||
def dp_group(self) -> Group:
|
||||
return _make_parallelism_group(
|
||||
self.rank, self.dp_size, stride=self.tp_size * self.ep_size
|
||||
)
|
||||
|
||||
|
||||
class VisionTowerMapping(MappingBase):
|
||||
"""Parallel mapping for vision encoders. Vision layers run colocated and
|
||||
share the attention TP group; non-colocated deployments should run the
|
||||
encoder out-of-engine (EPD-style workers + gateway dispatch).
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
rank: int | None = None,
|
||||
world_size: int = 1,
|
||||
tp_size: int | None = None,
|
||||
):
|
||||
super().__init__(rank, world_size)
|
||||
(self.tp_size,) = _resolve_parallelism_sizes(self.world_size, tp_size)
|
||||
|
||||
@cached_property
|
||||
def has_tp(self) -> bool:
|
||||
return self.tp_size > 1
|
||||
|
||||
@cached_property
|
||||
def tp_rank(self) -> int:
|
||||
return _make_parallelism_rank(self.rank, self.tp_size, stride=1)
|
||||
|
||||
@cached_property
|
||||
def tp_group(self) -> Group:
|
||||
return _make_parallelism_group(self.rank, self.tp_size, stride=1)
|
||||
|
||||
|
||||
class Mapping(MappingBase):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
rank: int | None = None,
|
||||
world_size: int = 1,
|
||||
*,
|
||||
attn_tp_size: int | None = None,
|
||||
attn_cp_size: int | None = None,
|
||||
attn_dp_size: int | None = None,
|
||||
dense_tp_size: int | None = None,
|
||||
dense_dp_size: int | None = None,
|
||||
moe_tp_size: int | None = None,
|
||||
moe_ep_size: int | None = None,
|
||||
moe_dp_size: int | None = None,
|
||||
nprocs_per_node: int | None = None,
|
||||
nnodes: int | None = None,
|
||||
base_gpu_id: int = 0,
|
||||
gpu_id_step: int = 1,
|
||||
):
|
||||
super().__init__(rank, world_size)
|
||||
self.attn = AttentionLayerMapping(
|
||||
rank=rank,
|
||||
world_size=world_size,
|
||||
tp_size=attn_tp_size,
|
||||
cp_size=attn_cp_size,
|
||||
dp_size=attn_dp_size,
|
||||
)
|
||||
self.dense = DenseLayerMapping(
|
||||
rank=rank,
|
||||
world_size=world_size,
|
||||
tp_size=dense_tp_size,
|
||||
dp_size=dense_dp_size,
|
||||
)
|
||||
self.moe = MoeLayerMapping(
|
||||
rank=rank,
|
||||
world_size=world_size,
|
||||
tp_size=moe_tp_size,
|
||||
ep_size=moe_ep_size,
|
||||
dp_size=moe_dp_size,
|
||||
)
|
||||
# Vision tower runs colocated on the attention TP group.
|
||||
self.vision = VisionTowerMapping(
|
||||
rank=rank,
|
||||
world_size=self.attn.tp_size,
|
||||
tp_size=self.attn.tp_size,
|
||||
)
|
||||
self.nprocs_per_node, self.nnodes = _resolve_parallelism_sizes(
|
||||
self.world_size, nprocs_per_node, nnodes
|
||||
)
|
||||
assert base_gpu_id >= 0
|
||||
assert gpu_id_step > 0
|
||||
self.base_gpu_id = base_gpu_id
|
||||
self.gpu_id_step = gpu_id_step
|
||||
|
||||
def _on_rank_initialized(self, rank: int):
|
||||
self.attn.rank = rank
|
||||
self.dense.rank = rank
|
||||
self.moe.rank = rank
|
||||
self.vision.rank = rank
|
||||
|
||||
@cached_property
|
||||
def has_attn_tp(self) -> bool:
|
||||
return self.attn.has_tp
|
||||
|
||||
@cached_property
|
||||
def has_attn_cp(self) -> bool:
|
||||
return self.attn.has_cp
|
||||
|
||||
@cached_property
|
||||
def has_attn_dp(self) -> bool:
|
||||
return self.attn.has_dp
|
||||
|
||||
@cached_property
|
||||
def node_rank(self) -> int:
|
||||
return self.rank // self.nprocs_per_node
|
||||
|
||||
@cached_property
|
||||
def local_rank(self) -> int:
|
||||
return self.rank % self.nprocs_per_node
|
||||
|
||||
@cached_property
|
||||
def gpu_id(self) -> int:
|
||||
return self.base_gpu_id + self.local_rank * self.gpu_id_step
|
||||
|
||||
def __repr__(self) -> str:
|
||||
rank_str = str(self._rank) if self._rank is not None else "?"
|
||||
lines = [
|
||||
f"Mapping(rank={rank_str}, world_size={self.world_size})",
|
||||
f" Cluster : {self.nnodes} node(s) x {self.nprocs_per_node} proc(s)",
|
||||
f" Attention: tp={self.attn.tp_size} cp={self.attn.cp_size} dp={self.attn.dp_size}",
|
||||
f" Vision: tp={self.vision.tp_size}",
|
||||
f" Dense : tp={self.dense.tp_size} dp={self.dense.dp_size}",
|
||||
f" MoE : tp={self.moe.tp_size} ep={self.moe.ep_size} dp={self.moe.dp_size}",
|
||||
]
|
||||
return "\n".join(lines)
|
||||
Reference in New Issue
Block a user