Coverage for / home / jenkins / .local / lib / python3.10 / site-packages / hyper_parallel / core / dtensor / _collective_utils.py: 20%
49 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-08-21 04:29 +0800
« prev ^ index » next coverage.py v7.13.1, created at 2026-08-21 04:29 +0800
1# Copyright 2026 Huawei Technologies Co., Ltd
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14# ============================================================================
15"""Mesh-scoped collectives for :func:`distribute_tensor` (PyTorch DTensor parity)."""
16from __future__ import annotations
18from typing import Optional, Sequence
20from hyper_parallel.core.dtensor.device_mesh import DeviceMesh
21from hyper_parallel.platform import get_platform
23platform = get_platform()
24Tensor = platform.Tensor
27def _ensure_mesh_process_groups(mesh: DeviceMesh) -> None:
28 """Lazily create per-axis process groups when mesh was built with ``init_backend=False``."""
29 if hasattr(mesh, "_dim_group_names") and mesh._dim_group_names is not None:
30 return
31 mesh._dim_group_names = DeviceMesh._init_process_groups( # pylint: disable=protected-access
32 mesh._mesh_shape,
33 mesh.mesh_dim_names,
34 mesh._rank_list,
35 )
38def mesh_scatter(
39 output: Tensor,
40 scatter_list: Sequence[Tensor],
41 mesh: DeviceMesh,
42 mesh_dim: int,
43 *,
44 group_src: int = 0,
45) -> Tensor:
46 """Scatter tensor chunks along one mesh dimension (PyTorch ``mesh_scatter`` parity)."""
47 _ensure_mesh_process_groups(mesh)
48 group = mesh.get_group(mesh_dim)
49 contiguous_list = [
50 chunk.contiguous() if hasattr(chunk, "is_contiguous") and not chunk.is_contiguous() else chunk
51 for chunk in scatter_list
52 ]
53 if platform.get_group_rank(group) == group_src:
54 platform.scatter(output, list(contiguous_list), group=group, group_src=group_src)
55 else:
56 platform.scatter(output, None, group=group, group_src=group_src)
57 return output
60def mesh_scatter_ragged(
61 output: Tensor,
62 scatter_list: Optional[Sequence[Tensor]],
63 mesh: DeviceMesh,
64 mesh_dim: int,
65 *,
66 group_src: int = 0,
67) -> Tensor:
68 """Scatter variable-length flat tensors with point-to-point communication.
70 Args:
71 output: Preallocated receive buffer for the current rank.
72 scatter_list: Source-rank tensors ordered by group rank. Non-source ranks
73 may pass ``None``.
74 mesh: Device mesh containing the communication group.
75 mesh_dim: Mesh dimension along which to scatter.
76 group_src: Source rank relative to the mesh-dimension group.
78 Returns:
79 The populated current-rank output buffer.
81 Raises:
82 ValueError: If the source rank or source scatter list is invalid.
83 """
84 _ensure_mesh_process_groups(mesh)
85 group = mesh.get_group(mesh_dim)
86 group_size = mesh.size(mesh_dim)
87 if group_src < 0 or group_src >= group_size:
88 raise ValueError(
89 f"group_src must be in [0, {group_size}), but got {group_src}"
90 )
92 group_rank = platform.get_group_rank(group)
93 source_global_rank = platform.get_global_rank(group, group_src)
94 if group_rank == group_src:
95 if scatter_list is None or len(scatter_list) != group_size:
96 raise ValueError(
97 "source scatter_list length must equal the mesh dimension size, "
98 f"got scatter_list={scatter_list!r}, group_size={group_size}"
99 )
100 output.copy_(scatter_list[group_src])
101 works = []
102 for destination_group_rank, chunk in enumerate(scatter_list):
103 if destination_group_rank == group_src:
104 continue
105 destination_global_rank = platform.get_global_rank(
106 group, destination_group_rank
107 )
108 works.append(
109 platform.isend(
110 chunk.contiguous(),
111 dst=destination_global_rank,
112 group=group,
113 )
114 )
115 for work in works:
116 work.wait()
117 return output
119 work = platform.irecv(
120 output,
121 src=source_global_rank,
122 group=group,
123 )
124 work.wait()
125 return output
128def mesh_broadcast(
129 tensor: Tensor,
130 mesh: DeviceMesh,
131 mesh_dim: int,
132 *,
133 group_src: int = 0,
134) -> Tensor:
135 """Broadcast a tensor along one mesh dimension (PyTorch ``mesh_broadcast`` parity)."""
136 _ensure_mesh_process_groups(mesh)
137 group = mesh.get_group(mesh_dim)
138 if hasattr(tensor, "is_contiguous") and not tensor.is_contiguous():
139 tensor = tensor.contiguous()
140 platform.broadcast(tensor, group=group, group_src=group_src)
141 return tensor