Coverage for / home / jenkins / .local / lib / python3.10 / site-packages / hyper_parallel / platform / mindspore / pipeline_parallel / _utils.py: 17%
86 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-07-06 05:41 +0800
« prev ^ index » next coverage.py v7.13.1, created at 2026-07-06 05:41 +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"""pipeline parallel utils"""
16import io
17import pickle
19from mindspore import nn, Tensor, mint, ops
20from mindspore.common import dtype as mstype
21from mindspore.communication import GlobalComm
22from mindspore.mint.distributed.distributed import _object_to_tensor, send, recv
24import hyper_parallel
25from hyper_parallel.core.shard.custom_shard import custom_shard
28class _MicroBatch(nn.Cell):
29 """
30 Split inputs into micro_batch in pipeline parallel.
32 Args:
33 micro_batch_num (int): The number of micro-batch.
34 args_batch_dim (list, optional): Specify the batch dim of the args.
35 Default ``None``.
36 kwargs_batch_dim(dict, optional): Specify the batch dim of the kwargs.
37 Default ``None``.
38 Inputs:
39 - **args** (list) - Input args.
40 - **kwargs** (dict) - Input kwargs.
42 Outputs:
43 - **args_after_split** (list) - Input args after split into micro_batches.
44 - **kwargs_after_split** (list) - Input kwargs after split into micro_batches.
45 """
47 def __init__(self, micro_batch_num, args_batch_dim=None, kwargs_batch_dim=None):
48 super().__init__()
49 self.micro_batch_num = micro_batch_num
50 self.args_batch_dim = args_batch_dim
51 self.kwargs_batch_dim = kwargs_batch_dim
53 def construct(self, args, kwargs):
54 """Construct of _MicroBatch"""
55 args_after_split = []
56 kwargs_after_split = []
57 for micro_idx in range(self.micro_batch_num):
58 micro_args = []
59 micro_kwargs = {}
60 for arg_idx, cur_arg in enumerate(args):
61 cur_arg_batch_dim = 0
62 if self.args_batch_dim and self.args_batch_dim[arg_idx] is not None:
63 cur_arg_batch_dim = self.args_batch_dim[arg_idx].batch_dim
64 if isinstance(cur_arg, hyper_parallel.DTensor):
65 micro_arg = self.split_inputs_with_custom_shard(cur_arg, cur_arg_batch_dim, micro_idx)
66 else:
67 micro_arg = self.split_inputs(cur_arg, cur_arg_batch_dim, micro_idx)
68 micro_args.append(micro_arg)
69 args_after_split.append(micro_args)
71 for key, cur_kwarg in kwargs.items():
72 cur_kwarg_batch_dim = 0
73 if self.kwargs_batch_dim is not None:
74 cur_kwarg_batch_dim = self.kwargs_batch_dim[key].batch_dim
75 if isinstance(cur_kwarg, hyper_parallel.DTensor):
76 micro_kwarg = self.split_inputs_with_custom_shard(cur_kwarg, cur_kwarg_batch_dim, micro_idx)
77 else:
78 micro_kwarg = self.split_inputs(cur_kwarg, cur_kwarg_batch_dim, micro_idx)
79 micro_kwargs[key] = micro_kwarg
80 kwargs_after_split.append(micro_kwargs)
81 return args_after_split, kwargs_after_split
83 def split_inputs_with_custom_shard(self, input_tensor, cur_arg_batch_dim, micro_idx):
84 """Split a DTensor input along the batch dimension using its custom shard layout."""
85 if not isinstance(input_tensor, hyper_parallel.DTensor):
86 raise TypeError(f"Input type {type(input_tensor)} is not DTensor.")
87 input_layout = input_tensor.layout
88 func_wrap = custom_shard(self.split_inputs,
89 device_mesh=input_layout.mesh,
90 out_placements=(input_layout.placements,),
91 in_placements=(input_layout.placements, None, None)
92 )
93 return func_wrap(input_tensor, cur_arg_batch_dim, micro_idx)
95 def split_inputs(self, input_tensor, cur_arg_batch_dim, micro_idx):
96 """
97 Split the input along the specified batch_dim and micro_idx
98 """
99 if cur_arg_batch_dim == -1:
100 return input_tensor
101 batch_dim_shape = input_tensor.shape[cur_arg_batch_dim]
102 if batch_dim_shape % self.micro_batch_num != 0:
103 raise ValueError(f"Batch dimension size {batch_dim_shape} is not divisible by \
104 micro_batch_num {self.micro_batch_num}")
105 micro_batch_begin = (batch_dim_shape // self.micro_batch_num) * micro_idx
106 micro_batch_end = (batch_dim_shape // self.micro_batch_num) * (micro_idx + 1)
107 strided_slice_begin = [0] * input_tensor.ndim
108 strided_slice_strides = [1] * input_tensor.ndim
109 strided_slice_end = list(input_tensor.shape)
110 strided_slice_begin[cur_arg_batch_dim] = micro_batch_begin
111 strided_slice_end[cur_arg_batch_dim] = micro_batch_end
112 micro_input = ops.strided_slice(input_tensor, strided_slice_begin, strided_slice_end, strided_slice_strides)
113 return micro_input
116def send_object_list(obj, dst=0, group=None):
117 """
118 Send the input Python object to dst rank.
120 Args:
121 obj (Any): The input tensor to be send.
122 dst (int, optional): Specifies the global rank that send the Python object to.
123 Default: ``0``.
124 group (str, optional): Communication group. Default: ``None``.
125 """
126 if group is None:
127 group = GlobalComm.WORLD_COMM_GROUP
128 if not isinstance(group, str):
129 raise TypeError(f"For 'send_object', the argument 'group' must be type of string, \
130 but got 'group' type : {type(group)}.")
131 if not isinstance(dst, int):
132 raise TypeError("For send_object, the dst must be int.")
133 obj_tensor, tensor_size = _object_to_tensor(obj)
134 obj_size = Tensor([tensor_size], dtype=mstype.int32)
135 send(obj_size, dst, group)
136 send(obj_tensor, dst, group)
139def recv_object_list(recv_obj, src=0, group=None):
140 """
141 receive Python object from src rank.
143 Args:
144 recv_obj (list): list to recv python objects.
145 src (int, optional): Specifies the global rank that receive the Python object.
146 Default: ``0`` .
147 group (str, optional): Communication group. Default: ``None``.
148 """
149 if group is None:
150 group = GlobalComm.WORLD_COMM_GROUP
151 if not isinstance(group, str):
152 raise TypeError(f"For 'recv_object', the argument 'group' must be type of string, \
153 but got 'group' type : {type(group)}.")
154 if not isinstance(src, int):
155 raise TypeError("For recv_object, the src must be int.")
156 obj_size = mint.zeros((1,), dtype=mstype.int32)
157 recv(obj_size, src, group)
158 # MindSpore PyNative ``recv`` only does a comm-stream wait; bridge to host
159 # so the subsequent ``.item()`` reads the freshly-received value instead
160 # of the original buffer.
161 size_val = int(obj_size.item())
162 obj_tensor = mint.zeros((size_val,), dtype=mstype.int8)
163 recv(obj_tensor, src, group)
164 buf = obj_tensor.asnumpy().tobytes()[:size_val]
165 recv_obj.clear()
166 recv_obj.append(pickle.Unpickler(io.BytesIO(buf)).load()[0])