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 "scatter_double_ring_direct.h"
12 : #include "alg_template_register.h"
13 :
14 : namespace hccl {
15 :
16 : constexpr u32 RANK_SIZE_THREE = 3;
17 :
18 0 : ScatterDoubleRingDirect::ScatterDoubleRingDirect(const HcclDispatcher dispatcher)
19 0 : : AlgTemplateBase(dispatcher)
20 : {
21 0 : }
22 :
23 0 : ScatterDoubleRingDirect::~ScatterDoubleRingDirect()
24 : {
25 0 : }
26 :
27 0 : HcclResult ScatterDoubleRingDirect::Prepare(HcomCollOpInfo *opInfo, const u32 userRank, const u32 subRingRank,
28 : std::vector<Stream> &subStreams, const std::vector<std::shared_ptr<LocalNotify>> &mainSignals,
29 : const std::vector<std::shared_ptr<LocalNotify>> &subSignals, const std::vector<std::vector<u32>> &ringsOrders,
30 : const std::vector<std::vector<Slice>> &multiRingSlices, const std::vector<std::vector<Slice>> &userMemInputSlices)
31 : {
32 0 : opInfo_ = opInfo;
33 0 : userRank_ = userRank;
34 0 : subRingRank_ = subRingRank;
35 0 : subStreams_ = subStreams;
36 0 : mainSignals_ = mainSignals;
37 0 : subSignals_ = subSignals;
38 0 : ringsOrders_ = ringsOrders;
39 0 : multiRingSlices_ = multiRingSlices;
40 0 : userMemInputSlices_ = userMemInputSlices;
41 0 : return HCCL_SUCCESS;
42 : }
43 :
44 : // reduce scatter ring direct算法的函数入口
45 0 : HcclResult ScatterDoubleRingDirect::RunAsync(const u32 rank, const u32 rankSize, const std::vector<LINK> &links)
46 : {
47 : // 基本的检查
48 0 : CHK_RET(CheckParameters(rank, rankSize, links));
49 :
50 : // 判断rank_size == 1
51 0 : if (rankSize == 1) {
52 0 : CHK_RET(MemcpyByOneRank());
53 0 : return HCCL_SUCCESS;
54 : }
55 : // 收集邻居信息
56 0 : CHK_RET(GetInitializedNeighborLinks(rank, rankSize, links));
57 :
58 : // 运行scatter, ring算法
59 0 : CHK_RET(RunScatter(rank, rankSize));
60 0 : CHK_RET(LaunchTaskExtend(dispatcher_, stream_, subStreams_));
61 :
62 0 : HCCL_INFO("ScatterDoubleRingDirect finished: rank[%u]", rank);
63 0 : return HCCL_SUCCESS;
64 : }
65 :
66 0 : HcclResult ScatterDoubleRingDirect::CheckParameters(const u32 rank, const u32 rankSize,
67 : const std::vector<LINK> &links)
68 : {
69 0 : CHK_PTR_NULL(opInfo_);
70 0 : CHK_RET(CheckConcurrentDirectParameters(rank, rankSize, links));
71 : // 判断ranksize大小
72 0 : CHK_PRT_RET(rankSize < 1,
73 : HCCL_ERROR("[ScatterDoubleRingDirect] rankSize size[%u] is less than 1", rankSize),
74 : HCCL_E_PARA);
75 : // 判断subStreams数量是否正确
76 0 : if (GetWorkflowMode() == HcclWorkflowMode::HCCL_WORKFLOW_MODE_OPS_KERNEL_INFO_LIB) {
77 0 : CHK_PRT_RET(subStreams_.size() != 1,
78 : HCCL_ERROR("[ScatterDoubleRingDirect] subStreams size[%u] must equal to 1", subStreams_.size()),
79 : HCCL_E_PARA);
80 : } else {
81 0 : CHK_PRT_RET(subStreams_.size() != DOUBLE_RING_STREAM_NUM,
82 : HCCL_ERROR("[ScatterDoubleRingDirect] subStreams size[%u] must equal to 3", subStreams_.size()),
83 : HCCL_E_PARA);
84 : }
85 0 : for (auto &s : subStreams_) {
86 0 : CHK_PTR_NULL(s.ptr());
87 : }
88 : // 判断mainSignals数量是否正确
89 0 : CHK_PRT_RET(mainSignals_.size() < 1,
90 : HCCL_ERROR("[ScatterDoubleRingDirect] mainSignals size[%u] is less than 1", mainSignals_.size()),
91 : HCCL_E_PARA);
92 : // 判断subSignals数量是否正确
93 0 : CHK_PRT_RET(subSignals_.size() < 1,
94 : HCCL_ERROR("[ScatterDoubleRingDirect] subSignals size[%u] is less than 1", subSignals_.size()),
95 : HCCL_E_PARA);
96 : // 判断ringsOrder size, multiRingSlices size, userMemInputSlices size是否正确
97 0 : if (ringsOrders_.size() != DOUBLE_RING_NUM || multiRingSlices_.size() != DOUBLE_RING_NUM ||
98 0 : userMemInputSlices_.size() != DOUBLE_RING_NUM) {
99 0 : HCCL_ERROR("[ScatterDoubleRingDirect] ringsOrder size[%u], multiRingSlices size[%u], userMemInputSlices"
100 : "size[%u] must equal to 2", ringsOrders_.size(), multiRingSlices_.size(), userMemInputSlices_.size());
101 0 : return HCCL_E_PARA;
102 : }
103 : // 判断ringsOrder数量是否正确
104 0 : for (u32 ringIndex = 0; ringIndex < ringsOrders_.size(); ringIndex++) {
105 0 : CHK_PRT_RET(ringsOrders_[ringIndex].size() != rankSize,
106 : HCCL_ERROR("[ScatterDoubleRingDirect] ringsOrders[%u] size[%u] must equal to rank size[%u]",
107 : ringIndex, ringsOrders_[ringIndex].size(), rankSize), HCCL_E_PARA);
108 : }
109 : // 判断multiRingSlices数量是否正确
110 0 : for (u32 ringIndex = 0; ringIndex < multiRingSlices_.size(); ringIndex++) {
111 0 : CHK_PRT_RET(multiRingSlices_[ringIndex].size() != rankSize,
112 : HCCL_ERROR("[ScatterDoubleRingDirect] multiRingSlices[%u] size[%u] must equal to rank size[%u]",
113 : ringIndex, multiRingSlices_[ringIndex].size(), rankSize), HCCL_E_PARA);
114 : }
115 : // 判断userMemInputSlices数量是否正确
116 0 : for (u32 ringIndex = 0; ringIndex < userMemInputSlices_.size(); ringIndex++) {
117 0 : CHK_PRT_RET(userMemInputSlices_[ringIndex].size() != rankSize,
118 : HCCL_ERROR("[ScatterDoubleRingDirect] userMemInputSlices_[%u] size[%u] must equal to rank size[%u]",
119 : ringIndex, userMemInputSlices_[ringIndex].size(), rankSize), HCCL_E_PARA);
120 : }
121 0 : HCCL_INFO("ScatterDoubleRingDirect CheckParameters success");
122 0 : return HCCL_SUCCESS;
123 : }
124 :
125 0 : HcclResult ScatterDoubleRingDirect::MemcpyByOneRank()
126 : {
127 0 : for (u32 ringIndex = 0; ringIndex < multiRingSlices_.size(); ringIndex++) {
128 0 : const Slice &srcSlice = userMemInputSlices_[ringIndex][0];
129 0 : const Slice &dstSlice = multiRingSlices_[ringIndex][0];
130 0 : DeviceMem src = DeviceMem::create(static_cast<u8 *>(opInfo_->inputAddr) + srcSlice.offset, srcSlice.size);
131 0 : DeviceMem dst;
132 0 : if (opInfo_->outputAddr != nullptr) {
133 : // opInfo_->outputAddr != nullptr指示要将输出发送至user output
134 0 : u64 stepOffset = multiRingSlices_[ringIndex][ringsOrders_[ringIndex][0]].offset;
135 0 : HCCL_DEBUG("Memcpy operation: stream[main], rank[%u] starts to rcv offset[%llu], size[%llu] at userMemOut_",
136 : userRank_, stepOffset, dstSlice.size);
137 0 : dst = DeviceMem::create(static_cast<u8 *>(opInfo_->outputAddr) + stepOffset, dstSlice.size);
138 : } else {
139 : // opInfo_->outputAddr == nullptr指示要将输出发送至CCL buffer
140 0 : HCCL_DEBUG("Memcpy operation: stream[main], rank[%u] starts to rcv offset[%llu], size[%llu] at outputMem_",
141 : userRank_, dstSlice.offset, dstSlice.size);
142 0 : dst = outputMem_.range(dstSlice.offset, dstSlice.size);
143 : }
144 0 : CHK_RET(HcclD2DMemcpyAsync(dispatcher_, dst, src, stream_));
145 0 : }
146 0 : return HCCL_SUCCESS;
147 : }
148 :
149 0 : HcclResult ScatterDoubleRingDirect::GetInitializedNeighborLinks(const u32 rank, const u32 rankSize,
150 : const std::vector<LINK> &links)
151 : {
152 : // 收集左邻居信息
153 0 : leftLink_ = links[(rank + rankSize - 1) % rankSize];
154 0 : CHK_SMART_PTR_NULL(leftLink_);
155 :
156 : // 收集右邻居信息
157 0 : rightLink_ = links[(rank + 1) % rankSize];
158 0 : CHK_SMART_PTR_NULL(rightLink_);
159 0 : HCCL_INFO("ScatterDoubleRingDirect finished to GetInitializedNeighborLinks");
160 0 : return HCCL_SUCCESS;
161 : }
162 :
163 0 : HcclResult ScatterDoubleRingDirect::RunInitStep(const u32 rank, const u32 rankSize)
164 : {
165 0 : if (rank != root_) {
166 0 : return HCCL_SUCCESS;
167 : }
168 0 : for (u32 ringIndex = 0; ringIndex < multiRingSlices_.size(); ringIndex++) {
169 0 : u32 initSlice0Idx = 0;
170 0 : if (ringIndex == 0) {
171 0 : initSlice0Idx = (rank + rankSize - 1) % rankSize;
172 : } else {
173 0 : initSlice0Idx = (subRingRank_ + rankSize - 1) % rankSize;
174 : }
175 0 : const Slice &srcInitSlice0 = userMemInputSlices_[ringIndex][initSlice0Idx];
176 : DeviceMem srcInit
177 0 : = DeviceMem::create(static_cast<u8 *>(opInfo_->inputAddr) + srcInitSlice0.offset, srcInitSlice0.size);
178 0 : const Slice &dstInitSlice0 = multiRingSlices_[ringIndex][initSlice0Idx];
179 0 : DeviceMem dstInit = inputMem_.range(dstInitSlice0.offset, dstInitSlice0.size);
180 0 : HCCL_DEBUG("Memcpy operation: step[-1] stream[sub] src rank[%u] starts to copy(rcv) offset[%llu], size[%llu] "
181 : "on userMemInput to offset[%llu], size[%llu] on CCL",
182 : userRank_, srcInitSlice0.offset, srcInitSlice0.size, dstInitSlice0.offset, dstInitSlice0.size);
183 0 : if (GetWorkflowMode() != HcclWorkflowMode::HCCL_WORKFLOW_MODE_OPS_KERNEL_INFO_LIB) {
184 0 : CHK_RET(HcclD2DMemcpyAsync(dispatcher_, dstInit, srcInit, stream_));
185 : }
186 0 : }
187 : // 第-1步,片内将部分数据从userIn搬到cclIn
188 0 : return HCCL_SUCCESS;
189 : }
190 :
191 0 : HcclResult ScatterDoubleRingDirect::RunAllStreams(const u32 rank, const u32 step, const u32 rankSize,
192 : RxMemoryInfo &mainRxMem, RxMemoryInfo &subRxMem, DeviceMem &mainLocalSrcMem, DeviceMem &mainLocalDstMem,
193 : DeviceMem &subLocalSrcMem, DeviceMem &subLocalDstMem)
194 : {
195 : (void)step;
196 : (void)rankSize;
197 0 : Stream mainStream = stream_;
198 0 : LINK mainPreLink = rightLink_;
199 0 : LINK mainNextLink = leftLink_;
200 0 : Stream subStream = subStreams_[0];
201 0 : LINK subPreLink = leftLink_;
202 0 : LINK subNextLink = rightLink_;
203 :
204 : // 唤醒所有环的主流做跨片同步
205 0 : CHK_RET(LocalNotify::Post(stream_, dispatcher_, subSignals_[0], profilerInput_.stage));
206 0 : CHK_RET(LocalNotify::Wait(subStreams_[0], dispatcher_, subSignals_[0], profilerInput_.stage));
207 :
208 0 : CHK_RET(mainNextLink->TxAck(mainStream));
209 0 : CHK_RET(subNextLink->TxAck(subStream));
210 :
211 0 : CHK_RET(mainPreLink->RxAck(mainStream));
212 0 : CHK_RET(subPreLink->RxAck(subStream));
213 :
214 : // 回到主流
215 0 : CHK_RET(LocalNotify::Post(subStreams_[0], dispatcher_, mainSignals_[0], profilerInput_.stage));
216 0 : CHK_RET(LocalNotify::Wait(stream_, dispatcher_, mainSignals_[0], profilerInput_.stage));
217 :
218 : // 主流唤醒所有流做跨片拷贝和片内拷贝
219 0 : CHK_RET(MainRecordSub());
220 :
221 : // 小数据量减少冗余通信,
222 0 : const u64 SMALL_DATASIZE = 4 * 1024 * 1024;
223 0 : bool isLargeCount = opInfo_->count * SIZE_TABLE[opInfo_->dataType] > SMALL_DATASIZE;
224 0 : if (isLargeCount || (step + 1 >= (root_ + rankSize - rank) % rankSize)) {
225 0 : CHK_RET(RxAsyncMemcpy(mainRxMem, mainStream, mainPreLink));
226 : }
227 0 : if (isLargeCount || (step + 1 >= (rank + rankSize - root_) % rankSize)) {
228 0 : CHK_RET(RxAsyncMemcpy(subRxMem, subStream, subPreLink));
229 : }
230 :
231 : // 本地拷贝
232 0 : if (rank == root_ && GetWorkflowMode() != HcclWorkflowMode::HCCL_WORKFLOW_MODE_OPS_KERNEL_INFO_LIB) {
233 0 : if (mainLocalDstMem != mainLocalSrcMem) {
234 0 : CHK_RET(HcclD2DMemcpyAsync(dispatcher_, mainLocalDstMem, mainLocalSrcMem, subStreams_[1]));
235 : }
236 0 : if (subLocalDstMem != subLocalSrcMem) {
237 0 : CHK_RET(HcclD2DMemcpyAsync(dispatcher_, subLocalDstMem, subLocalSrcMem, subStreams_[2]));
238 : }
239 : }
240 :
241 0 : CHK_RET(mainPreLink->TxDataSignal(mainStream));
242 0 : CHK_RET(subPreLink->TxDataSignal(subStream));
243 :
244 0 : CHK_RET(mainNextLink->RxDataSignal(mainStream));
245 0 : CHK_RET(subNextLink->RxDataSignal(subStream));
246 :
247 0 : CHK_RET(MainWaitSub());
248 0 : return HCCL_SUCCESS;
249 0 : }
250 :
251 0 : HcclResult ScatterDoubleRingDirect::RxAsyncMemcpy(RxMemoryInfo& mem, Stream &stream, LINK &link)
252 : {
253 0 : CHK_PTR_NULL(mem.dst);
254 0 : void *srcMemPtr = nullptr;
255 0 : CHK_RET(link->GetRemoteMem(mem.srcMemType, &srcMemPtr));
256 :
257 0 : DeviceMem srcDevMem(static_cast<s8 *>(srcMemPtr) + mem.srcOffset, mem.len);
258 0 : DeviceMem dstDevMem(static_cast<s8 *>(mem.dst), mem.len);
259 0 : CHK_RET(HcclD2DMemcpyAsync(dispatcher_, dstDevMem, srcDevMem,
260 : stream, link->GetRemoteRank(), link->GetLinkType()));
261 0 : return HCCL_SUCCESS;
262 0 : }
263 :
264 0 : HcclResult ScatterDoubleRingDirect::PrepareDeviceMems(const u32 rank, const u32 step,
265 : const u32 ringIndex, const u32 rankSize, const u32 subSliceIdx, const u32 rxSliceIdx, RxMemoryInfo &rxMem,
266 : DeviceMem &localSrcMem, DeviceMem &localDstMem)
267 : {
268 0 : const Slice &subSlice = userMemInputSlices_[ringIndex][subSliceIdx];
269 0 : const Slice &cclSlice = multiRingSlices_[ringIndex][subSliceIdx];
270 0 : const Slice &rxSlice = multiRingSlices_[ringIndex][rxSliceIdx];
271 :
272 0 : u64 lastStepOffset = multiRingSlices_[ringIndex][ringsOrders_[ringIndex][0]].offset;
273 :
274 0 : DeviceMem dst;
275 0 : if (step == rankSize - DMA_REDUCE_TWO_OFFSET && opInfo_->outputAddr != nullptr) {
276 0 : HCCL_DEBUG("MemcpyAsync operation: step[%u] stream[main], dst rank[%u] starts to rcv offset[%llu], "
277 : "size[%llu] at userMemOut_",
278 : step, userRank_, lastStepOffset, rxSlice.size);
279 0 : dst = DeviceMem::create(static_cast<u8 *>(opInfo_->outputAddr) + lastStepOffset, rxSlice.size);
280 : } else {
281 0 : HCCL_DEBUG("MemcpyAsync operation: step[%u] stream[main], dst rank[%u] starts to rcv offset[%llu], "
282 : "size[%llu] at inputMem_",
283 : step, userRank_, rxSlice.offset, rxSlice.size);
284 0 : dst = inputMem_.range(rxSlice.offset, rxSlice.size);
285 : }
286 0 : rxMem = RxMemoryInfo{UserMemType::INPUT_MEM, rxSlice.offset + baseOffset_, dst.ptr(), rxSlice.size};
287 :
288 0 : if (rank == root_) {
289 : // root节点参与通信,避免片内拷贝带宽过高过分抢占HBM带宽导致其他节点读取数据时性能下降
290 0 : u32 rxSliceIdxForRoot = (rxSliceIdx + DMA_REDUCE_TWO_OFFSET) % rankSize;
291 0 : Slice rxSliceForRoot = multiRingSlices_[ringIndex][rxSliceIdxForRoot];
292 0 : if ((rankSize < RANK_SIZE_THREE && step == 0) || (inputMem_.ptr() == opInfo_->inputAddr)) rxSliceForRoot.size = 0;
293 0 : dst = inputMem_.range(rxSliceForRoot.offset, rxSliceForRoot.size);
294 0 : rxMem = RxMemoryInfo{UserMemType::INPUT_MEM, rxSliceForRoot.offset + baseOffset_, dst.ptr(),
295 0 : rxSliceForRoot.size};
296 0 : HCCL_DEBUG("Memcpy operation: step[%u] stream[sub], src rank[%u] starts to send offset[%llu], size[%llu] "
297 : "from userMemIn_",
298 : step, userRank_, subSlice.offset, subSlice.size);
299 0 : localSrcMem = DeviceMem::create(static_cast<u8 *>(opInfo_->inputAddr) + subSlice.offset, subSlice.size);
300 0 : if (step == rankSize - DMA_REDUCE_TWO_OFFSET && opInfo_->outputAddr != nullptr) {
301 0 : HCCL_DEBUG("Memcpy operation: step[%u] stream[sub], dst rank[%u] starts to rcv offset[%llu], size[%llu] "
302 : "to userMemOut_",
303 : step, userRank_, lastStepOffset, subSlice.size);
304 0 : localDstMem = DeviceMem::create(static_cast<u8 *>(opInfo_->outputAddr) + lastStepOffset, subSlice.size);
305 : } else {
306 0 : HCCL_DEBUG("Memcpy operation: step[%u] stream[sub], dst rank[%u] starts to rcv offset[%llu], size[%llu] "
307 : "to inputMem_",
308 : step, userRank_, cclSlice.offset, cclSlice.size);
309 0 : localDstMem = inputMem_.range(cclSlice.offset, cclSlice.size);
310 : }
311 : }
312 0 : return HCCL_SUCCESS;
313 0 : }
314 :
315 0 : HcclResult ScatterDoubleRingDirect::RunScatter(const u32 rank, const u32 rankSize)
316 : {
317 0 : HCCL_INFO("ScatterDoubleRingDirect starts, the input param rank[%u]", rank);
318 :
319 0 : CHK_RET(RunInitStep(rank, rankSize));
320 :
321 : // 例如rank[0,1,2,3]中,rank0的rxSliceIdx = 2,txSliceIdx = 3, subSliceIdx = 1
322 0 : u32 subSliceIdx = (rank + rankSize - DMA_REDUCE_TWO_OFFSET) % rankSize;
323 0 : u32 mainSliceIdx = (subRingRank_ + rankSize - DMA_REDUCE_TWO_OFFSET) % rankSize;
324 :
325 0 : for (u32 step = 0; step < rankSize - 1; step++) {
326 : RxMemoryInfo rxMemSub;
327 0 : DeviceMem localSrcMemSub;
328 0 : DeviceMem localDstMemSub;
329 0 : CHK_RET(PrepareDeviceMems(rank, step, ALIGNED_SUB_RING_INDEX, rankSize,
330 : subSliceIdx, subSliceIdx, rxMemSub, localSrcMemSub, localDstMemSub));
331 : RxMemoryInfo rxMemMain;
332 0 : DeviceMem localSrcMemMain;
333 0 : DeviceMem localDstMemMain;
334 0 : CHK_RET(PrepareDeviceMems(rank, step, ALIGNED_MAIN_RING_INDEX, rankSize,
335 : mainSliceIdx, mainSliceIdx, rxMemMain, localSrcMemMain, localDstMemMain));
336 :
337 0 : CHK_RET(RunAllStreams(rank, step, rankSize, rxMemMain, rxMemSub, localSrcMemMain, localDstMemMain,
338 : localSrcMemSub, localDstMemSub));
339 :
340 : // 更新索引
341 0 : mainSliceIdx = (mainSliceIdx + rankSize - 1) % rankSize;
342 0 : subSliceIdx = (subSliceIdx + rankSize - 1) % rankSize;
343 0 : }
344 0 : HCCL_INFO("ScatterDoubleRingDirect finished to RunScatter");
345 0 : return HCCL_SUCCESS;
346 : }
347 :
348 : // 主流通知从流干活, 从流等待主流
349 0 : HcclResult ScatterDoubleRingDirect::MainRecordSub()
350 : {
351 0 : for (u32 signalIndex = 0; signalIndex < subSignals_.size(); signalIndex++) {
352 0 : CHK_RET(LocalNotify::Post(stream_, dispatcher_, subSignals_[signalIndex],
353 : profilerInput_.stage));
354 : }
355 0 : for (u32 signalIndex = 0; signalIndex < subSignals_.size(); signalIndex++) {
356 0 : CHK_RET(LocalNotify::Wait(subStreams_[signalIndex], dispatcher_, subSignals_[signalIndex],
357 : profilerInput_.stage));
358 : }
359 0 : for (u32 signalIndex = 0; signalIndex < subSignals_.size(); signalIndex++) {
360 0 : CHK_RET(ExecEmptyTask(inputMem_, outputMem_, subStreams_[signalIndex], dispatcher_));
361 : }
362 0 : CHK_RET(ExecEmptyTask(inputMem_, outputMem_, stream_, dispatcher_));
363 0 : return HCCL_SUCCESS;
364 : }
365 :
366 : // 主流等待从流, 从流告诉主流活干完了
367 0 : HcclResult ScatterDoubleRingDirect::MainWaitSub()
368 : {
369 0 : for (u32 signalIndex = 0; signalIndex < mainSignals_.size(); signalIndex++) {
370 0 : CHK_RET(LocalNotify::Wait(stream_, dispatcher_, mainSignals_[signalIndex], profilerInput_.stage));
371 : }
372 0 : for (u32 signalIndex = 0; signalIndex < mainSignals_.size(); signalIndex++) {
373 0 : CHK_RET(LocalNotify::Post(subStreams_[signalIndex], dispatcher_, mainSignals_[signalIndex],
374 : profilerInput_.stage));
375 : }
376 0 : for (u32 signalIndex = 0; signalIndex < mainSignals_.size(); signalIndex++){
377 0 : CHK_RET(ExecEmptyTask(inputMem_, outputMem_, subStreams_[signalIndex], dispatcher_));
378 : }
379 0 : CHK_RET(ExecEmptyTask(inputMem_, outputMem_, stream_, dispatcher_));
380 0 : return HCCL_SUCCESS;
381 : }
382 : REGISTER_TEMPLATE(TemplateType::TEMPLATE_SCATTER_DOUBLE_RING_DIRECT, ScatterDoubleRingDirect);
383 : } // namespace hccl
|