Line data Source code
1 : /**
2 : * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3 : * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4 : * CANN Open Software License Agreement Version 2.0 (the "License").
5 : * Please refer to the License for details. You may not use this file except in compliance with the License.
6 : * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7 : * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8 : * See LICENSE in the root of the software repository for the full text of the License.
9 : */
10 :
11 : #include "alg_template_register.h"
12 : #include "reduce_recursive_hd.h"
13 :
14 : namespace hccl {
15 1 : ReduceRecursiveHalvingDoubling::ReduceRecursiveHalvingDoubling(const HcclDispatcher dispatcher)
16 1 : : RecursiveHalvingDoublingBase(dispatcher)
17 1 : {}
18 :
19 2 : ReduceRecursiveHalvingDoubling::~ReduceRecursiveHalvingDoubling() {}
20 :
21 1 : HcclResult ReduceRecursiveHalvingDoubling::Prepare(u64 reduceAttrBitMap, [[maybe_unused]] HcomCollOpInfo* opInfo)
22 : {
23 1 : reduceAttr = reduceAttrBitMap;
24 1 : return HCCL_SUCCESS;
25 : }
26 :
27 : // 算法的主入口
28 0 : HcclResult ReduceRecursiveHalvingDoubling::RunAsync(
29 : const u32 rank, const u32 rankSize, const std::vector<std::shared_ptr<Transport>>& links)
30 : {
31 0 : CHK_SMART_PTR_NULL(dispatcher_);
32 0 : CHK_PTR_NULL(stream_.ptr());
33 0 : if (!outputMem_ || !inputMem_) {
34 0 : HCCL_ERROR("[ReduceRecursiveHalvingDoubling][RunAsync]rank[%u] run_async inputmem or outputmem is null", rank);
35 0 : return HCCL_E_PTR;
36 : }
37 0 : HCCL_INFO(
38 : "ReduceRecursiveHalvingDoubling run: rank[%u] root[%u] totalrank[%u] inputMem[%p] outputMem[%p]"
39 : "count[%llu]",
40 : rank, root_, rankSize, inputMem_.ptr(), outputMem_.ptr(), count_);
41 :
42 0 : HcclResult ret = HCCL_SUCCESS;
43 :
44 0 : if (rankSize == 1) {
45 0 : if (inputMem_ != outputMem_) {
46 0 : ret = HcclD2DMemcpyAsync(dispatcher_, outputMem_, inputMem_, stream_);
47 : }
48 0 : return ret;
49 : }
50 :
51 0 : senderInfo_.reset(new (std::nothrow) Sender(dataType_, reductionOp_, reduceAttr));
52 0 : CHK_SMART_PTR_NULL(senderInfo_);
53 :
54 0 : reducerInfo_.reset(new (std::nothrow) Reducer(dataType_, reductionOp_, reduceAttr));
55 0 : CHK_SMART_PTR_NULL(reducerInfo_);
56 :
57 0 : bool bRetSize = (links.size() < rankSize);
58 0 : CHK_PRT_RET(
59 : bRetSize,
60 : HCCL_ERROR("[ReduceRecursiveHalvingDoubling][RunAsync]rank[%u] linksize[%llu] is error", rank, links.size()),
61 : HCCL_E_INTERNAL);
62 :
63 0 : CHK_RET(CalcPartOneSizeAndBlockSize(rankSize));
64 :
65 0 : u32 bytesPerData = DataUnitSize(dataType_);
66 0 : u64 dataBytes = count_ * bytesPerData;
67 0 : CHK_RET(CalculateSlices(dataBytes));
68 :
69 : // 结果完成需要放在input
70 0 : CHK_RET(ReduceInPartOne(rank, links));
71 :
72 : // 此步骤完成后,结果放在ouput中
73 0 : CHK_RET(ReduceScatterInBlock(rank, rankSize, links));
74 :
75 : // 使用output进行gather
76 0 : CHK_RET(GatherInBlock(rank, rankSize, links));
77 :
78 0 : HCCL_INFO("ReduceRecursiveHalvingDoubling rank[%u] finished", rank);
79 0 : return HCCL_SUCCESS;
80 : }
81 :
82 0 : HcclResult ReduceRecursiveHalvingDoubling::ReduceInPartOne(u32 rank, const std::vector<LINK>& links)
83 : {
84 0 : HCCL_INFO("rank[%u] part1Size_[%u] root[%u]", rank, part1Size_, root_);
85 :
86 0 : if (rank >= part1Size_) { // rank在第二部分,不参与ReduceInPartOne
87 0 : HCCL_INFO("rank[%u] not in part1, don't need reduce", rank);
88 0 : return HCCL_SUCCESS;
89 : }
90 : // root在第二部分,需要选取第一部分偶数rank接收,以0作为判断标准,否则在第一部分,与root奇偶性相同rank接收
91 0 : u32 rootFlag = (root_ >= part1Size_) ? 0 : root_;
92 :
93 0 : if (rank % 2 == rootFlag % 2) { // 1.从下一个rank接收数据到output,2. reduce到本rank的input
94 0 : u32 peerRank = (rank % 2) == 0 ? (rank + 1) : (rank - 1);
95 0 : HCCL_INFO(
96 : "rank[%u] outputMem receives from PeerRank[%u] inputMem, Offset[%llu], Size[%llu]", rank, peerRank,
97 : baseOffset_, outputMem_.size());
98 :
99 0 : if (peerRank < links.size()) {
100 0 : CHK_SMART_PTR_NULL(links[peerRank]);
101 :
102 0 : HcclResult ret = links[peerRank]->TxAck(stream_);
103 0 : CHK_PRT_RET(
104 : ret != HCCL_SUCCESS, HCCL_ERROR("[Reduce][InPartOne]tx ack to peerrank[%u] failed", peerRank), ret);
105 0 : ret = links[peerRank]->RxAck(stream_);
106 0 : CHK_PRT_RET(
107 : ret != HCCL_SUCCESS, HCCL_ERROR("[Reduce][InPartOne]rx ack from peerank[%u] failed", peerRank), ret);
108 :
109 : // 接收数据到本端的 output
110 0 : HCCL_DEBUG("send mem[%p] size[%llu] to peerank[%u]", outputMem_.ptr(), outputMem_.size(), peerRank);
111 0 : ret = links[peerRank]->TxAsync(UserMemType::INPUT_MEM, baseOffset_, outputMem_.ptr(), 0, stream_);
112 0 : CHK_PRT_RET(
113 : ret != HCCL_SUCCESS,
114 : HCCL_ERROR(
115 : "[Reduce][InPartOneToEven]TxAsync: tx async size[%llu] "
116 : "failed",
117 : 0),
118 : ret);
119 0 : CHK_RET(reducerInfo_->run(
120 : dispatcher_, links[peerRank], baseOffset_, outputMem_, inputMem_, outputMem_, stream_));
121 0 : ret = links[peerRank]->RxWaitDone(stream_);
122 0 : CHK_PRT_RET(ret != HCCL_SUCCESS, HCCL_ERROR("[Reduce][InPartOne]RxWaitDone failed"), ret);
123 : }
124 0 : } else if ((rank % 2) != (rootFlag % 2)) { // 向上一个rank的output发数据 2
125 0 : u32 peerRank = (rank % 2 == 0) ? (rank + 1) : (rank - 1);
126 :
127 0 : if (peerRank < links.size()) {
128 0 : CHK_SMART_PTR_NULL(links[peerRank]);
129 0 : HcclResult ret = links[peerRank]->TxAck(stream_);
130 0 : CHK_PRT_RET(
131 : ret != HCCL_SUCCESS, HCCL_ERROR("[Reduce][InPartOne]tx ack to peerrank[%u] failed", peerRank), ret);
132 0 : ret = links[peerRank]->RxAck(stream_);
133 0 : CHK_PRT_RET(
134 : ret != HCCL_SUCCESS, HCCL_ERROR("[Reduce][InPartOne]rx ack from peerank[%u] failed", peerRank), ret);
135 : // 发送到对端的output
136 0 : HCCL_DEBUG(
137 : "rank[%u] sends inputMem[%p] to PeerRank[%u] Offset[%llu], Size[%llu]", rank, inputMem_.ptr(), peerRank,
138 : baseOffset_, inputMem_.size());
139 0 : ret = senderInfo_->run(links[peerRank], baseOffset_, inputMem_, stream_);
140 0 : CHK_PRT_RET(
141 : ret != HCCL_SUCCESS, HCCL_ERROR("[Reduce][InPartOne]tx sync to peerank[%u] failed", peerRank), ret);
142 0 : ret = links[peerRank]->RxAsync(UserMemType::OUTPUT_MEM, baseOffset_, inputMem_.ptr(), 0, stream_);
143 0 : CHK_PRT_RET(
144 : ret != HCCL_SUCCESS,
145 : HCCL_ERROR("[AlgTemplateBase][ExecuteTxSync]ExecuteTxSync: rx async size[%llu] failed", 0), ret);
146 0 : ret = links[peerRank]->DataReceivedAck(stream_);
147 0 : CHK_PRT_RET(
148 : ret != HCCL_SUCCESS,
149 : HCCL_ERROR("[AlgTemplateBase][ExecuteTxSync]ExecuteTxSync: data received ack failed"), ret);
150 0 : ret = links[peerRank]->TxWaitDone(stream_);
151 0 : CHK_PRT_RET(ret != HCCL_SUCCESS, HCCL_ERROR("[Reduce][InPartOne]TxWaitDone failed"), ret);
152 : }
153 : }
154 0 : return HCCL_SUCCESS;
155 : }
156 :
157 0 : HcclResult ReduceRecursiveHalvingDoubling::ReduceScatterInBlock(u32 rank, u32 rankSize, const std::vector<LINK>& links)
158 : {
159 0 : u32 rankInBlock = 0;
160 :
161 0 : u32 rootFlag = (root_ >= part1Size_) ? 0 : root_;
162 0 : HCCL_DEBUG(
163 : "[ReduceRecursiveHalvingDoubling][ReduceScatterInBlock]rootFlag is %u, rankInBlock is %u", rootFlag,
164 : rankInBlock);
165 : // 需要根据root判断,让root节点必然参加reducescatter,在第一部分的rank若与root奇偶性不同,直接返回
166 0 : if (rank < part1Size_ && (rank % 2) != (rootFlag % 2)) { // 模2判断奇偶性,本rank处于第一部分,奇偶性与root不同
167 0 : return HCCL_SUCCESS;
168 0 : } else if (rank < part1Size_) { // 模2判断奇偶性,本rank 处于第一部分,奇偶性与root相同
169 0 : rankInBlock = rank / 2; // 除2计算block内的rank值
170 : } else { // 本rank不属于第一部分
171 0 : rankInBlock = rank - part1Size_ / 2; // 除2计算block内的part1的范围
172 : }
173 : // 直接调用block的reducscatterhd算法
174 : std::unique_ptr<AlgTemplateBase> executor
175 0 : = AlgTemplateRegistry::Instance().GetAlgTemplate(TemplateType::TEMPLATE_REDUCESCATTER_HD, dispatcher_);
176 0 : CHK_SMART_PTR_NULL(executor);
177 0 : CHK_RET(executor->Prepare(
178 : inputMem_, outputMem_, outputMem_, count_, dataType_, stream_, reductionOp_, -1, slices_, baseOffset_,
179 : blockSize_, reduceAttr, UserMemType::INPUT_MEM, UserMemType::OUTPUT_MEM));
180 :
181 0 : CHK_RET(executor->RegisterProfiler(profilerInput_.planeID, profilerInput_.stage, profilerInput_.step, stream_));
182 :
183 : // 重新建立reducscatterscatter需要的链接
184 0 : std::vector<LINK> subLinks;
185 0 : CHK_RET(BuildRootSubLinks(links, subLinks, rankSize));
186 :
187 0 : CHK_PRT_RET(
188 : subLinks.size() == 0,
189 : HCCL_ERROR(
190 : "[ReduceRecursiveHalvingDoubling][ReduceScatterInBlock]rank[%u] "
191 : "BuildSubLinks failed",
192 : rank),
193 : HCCL_E_PARA);
194 :
195 0 : CHK_RET(executor->RunAsync(rankInBlock, blockSize_, subLinks));
196 :
197 0 : return HCCL_SUCCESS;
198 0 : }
199 :
200 0 : HcclResult ReduceRecursiveHalvingDoubling::CalculateStepSlices(
201 : const std::vector<Slice>& inputSlices, u32 stepNum, u32 rank, SliceType type, std::vector<Slice>& sliceOut)
202 : {
203 0 : std::vector<Slice> slice(stepNum);
204 :
205 0 : for (u32 step = 0; step < stepNum; step++) {
206 : // all-gather操作, halving_bitmask从低往高循环, size倍增
207 0 : u32 halvingBitmask = (1 << step);
208 0 : u32 peerRank = rank ^ halvingBitmask;
209 :
210 : // 计算tx_slice/rx_slice
211 0 : u32 sliceId = (type == SliceType::SLICE_TYPE_RX) ? (peerRank & (~(halvingBitmask - 1))) :
212 0 : (rank & (~(halvingBitmask - 1)));
213 :
214 0 : slice[step].offset = inputSlices[sliceId].offset;
215 0 : CHK_RET(Sum(inputSlices, sliceId, halvingBitmask, slice[step].size));
216 :
217 0 : HCCL_DEBUG(
218 : "Slice Info: rank[%u], slices[%u].offset=%llu, slices[%u].size=%llu", rank, step, slice[step].offset, step,
219 : slice[step].size);
220 : }
221 :
222 0 : sliceOut = std::move(slice);
223 0 : return HCCL_SUCCESS;
224 0 : }
225 0 : HcclResult ReduceRecursiveHalvingDoubling::BuildRootSubLinks(
226 : const std::vector<LINK>& links, std::vector<LINK>& subLinks, u32 rankSize) const
227 : {
228 0 : std::vector<LINK>::const_iterator iter = links.begin();
229 0 : subLinks.resize(blockSize_);
230 0 : u32 rootFlag = (root_ >= part1Size_) ? 0 : root_;
231 0 : for (u32 i = 0; i < rankSize; i++) {
232 0 : if (i < part1Size_
233 0 : && (i % 2)
234 0 : != rootFlag % 2) { // 模2与root模2比较代表当前rank在part1的内且与root奇偶性不同,不参与block内的建链
235 0 : continue;
236 0 : } else if (i < part1Size_) {
237 0 : std::vector<LINK>::const_iterator niter = std::next(iter, i);
238 0 : if (niter != links.end()) {
239 0 : subLinks[i / 2] = *niter; // 除2计算出在block内的rank号
240 : }
241 : } else {
242 0 : std::vector<LINK>::const_iterator niter = std::next(iter, i);
243 0 : if (niter != links.end()) {
244 0 : subLinks[i - part1Size_ / 2] = *niter; // rank在part2中,用原始rank减part1除2,计算出在block内的rank号
245 : }
246 : }
247 : }
248 :
249 0 : return HCCL_SUCCESS;
250 : }
251 : // 结果在output中,直接使用oupt进行数据收发
252 0 : HcclResult ReduceRecursiveHalvingDoubling::GatherInBlock(u32 rank, u32 rankSize, const std::vector<LINK>& links)
253 : {
254 0 : u32 rankInBlock = 0;
255 :
256 0 : u32 rootFlag = (root_ >= part1Size_) ? 0 : root_;
257 0 : if (rank < part1Size_
258 0 : && (rank % 2) != (rootFlag % 2)) { // 模2判断奇偶性,本rank 处于第一部分,并且和root rank奇偶不同
259 0 : return HCCL_SUCCESS;
260 0 : } else if (rank < part1Size_) { // 模2判断奇偶性,本rank 处于第一部分,并且奇偶性和root相同
261 0 : rankInBlock = rank / 2; // 在block内的rank为实际rank除以2
262 : } else {
263 0 : rankInBlock = rank - part1Size_ / 2; // 除2计算block内的part1的范围
264 : }
265 0 : u32 rootInBlock = (root_ > part1Size_) ? (root_ - part1Size_ / 2) : (root_ / 2);
266 : // 重新建立gather需要的链接
267 0 : std::vector<LINK> subLinks;
268 :
269 0 : CHK_RET(BuildRootSubLinks(links, subLinks, rankSize));
270 :
271 0 : CHK_PRT_RET(
272 : subLinks.size() == 0, HCCL_ERROR("[Gather][InBlock]rank[%u] build sub links failed", rank), HCCL_E_PARA);
273 :
274 0 : CHK_RET(CalculateStepSlices(slices_, round_, rankInBlock, SliceType::SLICE_TYPE_TX, txSlices_));
275 :
276 0 : CHK_RET(CalculateStepSlices(slices_, round_, rankInBlock, SliceType::SLICE_TYPE_RX, rxSlices_));
277 :
278 0 : for (u32 step = 0; step < round_; step++) {
279 0 : u32 peerRankBitmask = (1 << step);
280 0 : u32 opBitmask = peerRankBitmask - 1; // 判断本轮是否进行收发
281 : // 断rank是否和root在同一轮次接收发送的block内,第一轮为total,第二轮为1/2,第三轮为1/4....
282 0 : if ((step != 0) && ((rankInBlock & opBitmask) != (rootInBlock & opBitmask))) {
283 0 : return HCCL_SUCCESS; // rank在本轮同root不在一个操作块内,不操作,直接返回
284 : }
285 0 : u32 peerRank = rankInBlock ^ peerRankBitmask;
286 0 : CHK_SMART_PTR_NULL(subLinks[peerRank]);
287 : // 再次判断是否和root在同一1/2,1/4,用来判断数据是收还是发
288 0 : if ((rankInBlock & peerRankBitmask) == (rootInBlock & peerRankBitmask)) {
289 0 : DeviceMem rxMem = outputMem_.range(rxSlices_[step].offset, rxSlices_[step].size);
290 0 : HcclResult ret = subLinks[peerRank]->TxAck(stream_);
291 0 : CHK_PRT_RET(
292 : ret != HCCL_SUCCESS,
293 : HCCL_ERROR("[Gather][InBlock]rank[%u] tx ack from peerank[%u] failed", rank, peerRank), ret);
294 0 : ret = subLinks[peerRank]->RxAck(stream_);
295 0 : CHK_PRT_RET(
296 : ret != HCCL_SUCCESS,
297 : HCCL_ERROR("[Gather][InBlock]rank[%u] rx ack from peerank[%u] failed", rank, peerRank), ret);
298 :
299 : // 等待对端可以接收数据
300 0 : HCCL_DEBUG(
301 : "rank[%u] outputMem[%p] receive from PeerRank[%u] outputMem, Offset[%llu], "
302 : "Size[%llu]",
303 : rank, outputMem_.ptr(), peerRank, baseOffset_ + rxSlices_[step].offset, rxSlices_[step].size);
304 :
305 0 : ret = ExecuteRxSync(
306 0 : subLinks[peerRank], UserMemType::OUTPUT_MEM, baseOffset_ + rxSlices_[step].offset, rxMem.ptr(),
307 0 : rxSlices_[step].size, stream_);
308 0 : CHK_PRT_RET(
309 : ret != HCCL_SUCCESS,
310 : HCCL_ERROR("[Gather][InBlock]rank[%u] rx sync from PeerRank[%u] failed", rank, peerRank), ret);
311 0 : ret = subLinks[peerRank]->RxWaitDone(stream_);
312 0 : CHK_PRT_RET(ret != HCCL_SUCCESS, HCCL_ERROR("[Reduce][InPartOne]RxWaitDone failed"), ret);
313 0 : } else {
314 0 : DeviceMem txMem = outputMem_.range(txSlices_[step].offset, txSlices_[step].size);
315 0 : HcclResult ret = subLinks[peerRank]->TxAck(stream_);
316 0 : CHK_PRT_RET(
317 : ret != HCCL_SUCCESS,
318 : HCCL_ERROR("[Gather][InBlock]rank[%u] tx ack from peerank[%u] failed", rank, peerRank), ret);
319 0 : ret = subLinks[peerRank]->RxAck(stream_);
320 0 : CHK_PRT_RET(
321 : ret != HCCL_SUCCESS,
322 : HCCL_ERROR("[Gather][InBlock]rank[%u] rx ack from peerank[%u] failed", rank, peerRank), ret);
323 0 : HCCL_DEBUG(
324 : "rank[%u] outputMem[%p] sends to peerrank[%u] outputmem, offset[%llu], "
325 : "size[%llu]",
326 : rank, outputMem_.ptr(), peerRank, baseOffset_ + txSlices_[step].offset, txSlices_[step].size);
327 0 : ret = ExecuteTxSync(
328 0 : subLinks[peerRank], UserMemType::OUTPUT_MEM, baseOffset_ + txSlices_[step].offset, txMem.ptr(),
329 0 : txSlices_[step].size, stream_);
330 0 : CHK_PRT_RET(
331 : ret != HCCL_SUCCESS,
332 : HCCL_ERROR("[Gather][InBlock]rank[%u] tx sync to PeerRank[%u] failed", rank, peerRank), ret);
333 0 : ret = subLinks[peerRank]->TxWaitDone(stream_);
334 0 : CHK_PRT_RET(ret != HCCL_SUCCESS, HCCL_ERROR("[Reduce][InPartOne]TxWaitDone failed"), ret);
335 0 : }
336 : }
337 :
338 0 : return HCCL_SUCCESS;
339 0 : }
340 0 : HcclResult ReduceRecursiveHalvingDoubling::GetNslbAdjInfo(
341 : const u32 rank, const u32 rankSize, const std::vector<LINK>& links, AdjInfo& nslbAdjInfo)
342 : {
343 0 : u32 nslbRound = 0;
344 0 : u32 base = 1;
345 0 : const u32 minExponent = 1;
346 0 : while ((base << nslbRound) <= rankSize) {
347 0 : nslbRound++;
348 : }
349 0 : if (nslbRound >= minExponent) {
350 0 : nslbRound = nslbRound - minExponent;
351 : }
352 0 : u32 nslbBlockSize = base << nslbRound;
353 : // 获取第一部分:rank数减block数乘2
354 0 : u32 nslbPart1Size = (rankSize - nslbBlockSize) * NSLBDP_REDUCE_MOLD2;
355 : // 2的次幂场景下处理流程
356 0 : if (nslbPart1Size == 0) {
357 0 : u32 stepNum = 0;
358 0 : while ((rankSize >> (stepNum + 1)) != 0) {
359 0 : stepNum++;
360 : }
361 0 : HCCL_DEBUG("[ReduceRecursiveHalvingDoubling]GetNslbAdjInfo start");
362 0 : for (u32 step = 0; step < stepNum; step++) {
363 0 : u32 peerRankBitmask = 1 << (stepNum - step - 1);
364 0 : u32 peerRank = rank ^ peerRankBitmask;
365 0 : NslbDpAdjInfo adjInfoStep = {};
366 0 : u32 remoteuserRank = links[peerRank]->GetRemoteRank();
367 0 : HCCL_DEBUG("[ReduceRecursiveHalvingDoubling]now step %u, remoteuserRank is %u", step, remoteuserRank);
368 0 : adjInfoStep.dstLocalRankId = remoteuserRank;
369 0 : adjInfoStep.phaseId = step + 1;
370 0 : adjInfoStep.rev = 0;
371 0 : nslbAdjInfo.nsAdjInfo.push_back(adjInfoStep);
372 : }
373 0 : nslbAdjInfo.dstRankNum = stepNum;
374 0 : return HCCL_SUCCESS;
375 : }
376 : // 非2的次幂场景下,被合并部分的奇数rank处理流程
377 0 : if (rank < nslbPart1Size && rank % NSLBDP_REDUCE_MOLD2 == 1) {
378 0 : u32 peerRank = rank - 1;
379 0 : if (peerRank < links.size()) {
380 0 : NslbDpAdjInfo adjInfoStep = {};
381 0 : adjInfoStep.dstLocalRankId = links[peerRank]->GetRemoteRank();
382 0 : adjInfoStep.phaseId = 1;
383 0 : adjInfoStep.rev = 0;
384 0 : HCCL_INFO("AllGatherHDR-nslb: peerRank[%u]", peerRank);
385 0 : nslbAdjInfo.nsAdjInfo.push_back(adjInfoStep);
386 0 : nslbAdjInfo.dstRankNum = 1;
387 : }
388 0 : return HCCL_SUCCESS;
389 : }
390 : // 针对合并后映射成2的次幂场景处理
391 0 : u32 rankInBlock = 0;
392 0 : if (rank < nslbPart1Size && (rank % NSLBDP_REDUCE_MOLD2) == 0) {
393 0 : rankInBlock = rank / NSLBDP_REDUCE_MOLD2; // 直接除以2即为本rank的在block内的排序
394 : } else {
395 : rankInBlock
396 0 : = rank - nslbPart1Size / NSLBDP_REDUCE_MOLD2; // 通过rank减去part1除2的大小即不处于第一部分的block内rank号
397 : }
398 0 : std::vector<LINK> subLinks;
399 0 : std::vector<LINK>::const_iterator iter = links.begin();
400 0 : subLinks.resize(nslbBlockSize);
401 0 : for (u32 i = 0; i < rankSize; i++) {
402 0 : if (i < nslbPart1Size
403 0 : && (i % NSLBDP_REDUCE_MOLD2) == 1) { // 模2余1代表当前rank在part1的奇数位置上,不参与block内的建链
404 0 : continue;
405 0 : } else if (i < nslbPart1Size && (i % NSLBDP_REDUCE_MOLD2) == 0) { // 模2余0代表当前rank在part1的偶数位置上
406 0 : std::vector<LINK>::const_iterator niter = std::next(iter, i);
407 0 : if (niter != links.end()) {
408 0 : subLinks[i / NSLBDP_REDUCE_MOLD2] = *niter;
409 : }
410 0 : } else {
411 0 : std::vector<LINK>::const_iterator niter = std::next(iter, i);
412 0 : if (niter != links.end()) {
413 0 : subLinks[i - nslbPart1Size / NSLBDP_REDUCE_MOLD2] = *niter;
414 : }
415 : }
416 : }
417 0 : u32 stepNum = 0;
418 0 : while ((rankSize >> (stepNum + 1)) != 0) {
419 0 : stepNum++;
420 : }
421 : // 映射完成后针对以新的通信域进行邻接表获取
422 0 : for (u32 step = 0; step < stepNum; step++) {
423 0 : u32 peerRankBitmask = 1 << (stepNum - step - 1);
424 0 : u32 peerRank = rankInBlock ^ peerRankBitmask;
425 0 : if (subLinks[peerRank] == nullptr) {
426 0 : continue;
427 : }
428 0 : NslbDpAdjInfo adjInfoStep = {};
429 0 : u32 remoteuserRank = subLinks[peerRank]->GetRemoteRank();
430 0 : adjInfoStep.dstLocalRankId = remoteuserRank;
431 0 : adjInfoStep.phaseId = step + 1;
432 0 : adjInfoStep.rev = 0;
433 0 : nslbAdjInfo.nsAdjInfo.push_back(adjInfoStep);
434 : }
435 0 : nslbAdjInfo.dstRankNum = stepNum;
436 :
437 0 : if (nslbAdjInfo.nsAdjInfo.size() == 0) {
438 0 : return HCCL_SUCCESS;
439 : }
440 : // 上面处理完成后,紧接着处理合并部分的偶数rank同步到奇数rank增加phaseId
441 0 : if (rank < nslbPart1Size && rank % NSLBDP_REDUCE_MOLD2 == 0) {
442 0 : u32 peerRank = rank + 1;
443 0 : uint16_t phaseSize = nslbAdjInfo.nsAdjInfo.size();
444 0 : if (peerRank < links.size()) {
445 0 : NslbDpAdjInfo adjInfoStep = {};
446 0 : adjInfoStep.dstLocalRankId = links[peerRank]->GetRemoteRank();
447 0 : adjInfoStep.phaseId = nslbAdjInfo.nsAdjInfo[phaseSize - 1].phaseId + 1;
448 0 : adjInfoStep.rev = 0;
449 0 : HCCL_INFO("Scatter-nslb: peerRank[%u]", peerRank);
450 0 : nslbAdjInfo.nsAdjInfo.push_back(adjInfoStep);
451 0 : nslbAdjInfo.dstRankNum = nslbAdjInfo.nsAdjInfo.size();
452 : }
453 0 : return HCCL_SUCCESS;
454 : }
455 0 : return HCCL_SUCCESS;
456 0 : }
457 : REGISTER_TEMPLATE(TemplateType::TEMPLATE_REDUCE_RECURSIVE_HALVING_DOUBLING, ReduceRecursiveHalvingDoubling);
458 : } // namespace hccl
|