LCOV - code coverage report
Current view: top level - legacy/ascend910/framework/device/aicpu_kfc/algorithm - aicpu_reduce_scatter.cc (source / functions) Coverage Total Hit
Test: coverage.info Lines: 73.5 % 302 222
Test Date: 2026-07-28 12:11:00 Functions: 50.0 % 12 6

            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 "aicpu_reduce_scatter.h"
      12              : 
      13              : namespace {
      14              : template<typename T>
      15            0 : inline T MathCeil(T num1, T num2) {
      16            0 :     if (num2 == 0) {
      17            0 :         return num1;
      18              :     }
      19            0 :     return (num1 + num2 - 1) / num2;
      20              : }
      21              : 
      22              : template<typename T>
      23            0 : inline T AlignUp(T num1, T num2) {
      24            0 :     return MathCeil(num1, num2) * num2;
      25              : }
      26              : }
      27              : 
      28           21 : HcclResult AicpuReduceScatter::RunAlgorithm(HcclReduceOp opType, void *sendBuffer, void *recvBuffer, u64 dataCount,
      29              :     HcclDataType dataType, u64 strideLen, AivAicpuOpParam * /* nextTask */)
      30              : {
      31           21 :     CHK_PTR_NULL(ctx_);
      32              :     // dataCount 为tile的输入的数据量(scatter前)
      33           21 :     if (dataCount % rankNum_ != 0) { // 每个tile数据量必须能均分至每张卡
      34            2 :         HCCL_ERROR("Reduce scatter dataCount %lu max be multiple of rankNum_.", dataCount);
      35            2 :         return HCCL_E_NOT_SUPPORT;
      36              :     }
      37              : 
      38           19 :     switch (ctx_->commAlg) {
      39           15 :         case CommAlgType::COMM_ALG_FULL_MESH: {
      40           15 :             if (ctx_->determinism) {
      41            6 :                 return RunDeterministicReduceScatterLocal(opType, sendBuffer, recvBuffer, dataCount, dataType, strideLen);
      42              :             }
      43            9 :             return RunReduceScatterWriteMode(opType, sendBuffer, recvBuffer, dataCount, dataType, strideLen);
      44              :         }
      45            2 :         case CommAlgType::COMM_ALG_DOUBLE_RING: {
      46            2 :             return RunDoubleRingReduceScatter(opType, reinterpret_cast<u64>(sendBuffer),
      47            2 :                 reinterpret_cast<u64>(recvBuffer), dataCount, dataType);
      48              :         }
      49            2 :         case CommAlgType::COMM_ALG_SWITCH_WING: {
      50            2 :             return RunSwitchReduceScatter(opType, reinterpret_cast<u64>(sendBuffer), reinterpret_cast<u64>(recvBuffer),
      51            2 :                 dataCount, dataType);
      52              :         }
      53            0 :         default: {
      54            0 :             HCCL_ERROR("CommAlg %d is not supported.", ctx_->commAlg);
      55            0 :             return HCCL_E_NOT_SUPPORT;
      56              :         }
      57              :     }
      58              : }
      59              : 
      60            6 : HcclResult AicpuReduceScatter::RunDeterministicReduceScatterLocal(HcclReduceOp opType, void *sendBuffer,
      61              :     void *recvBuffer, u64 dataCount, HcclDataType dataType, u64 strideLen)
      62              : {
      63            6 :     u8 *curInputPtr = static_cast<u8 *>(sendBuffer);
      64            6 :     u8 *curOutputPtr = static_cast<u8 *>(recvBuffer);
      65            6 :     u64 windowSize = ctx_->windowSize;
      66            6 :     u64 countLeft = dataCount / rankNum_;
      67            6 :     u64 maxCountPerLoop = windowSize / unitSize_ / rankNum_;
      68            6 :     u64 inputOffset = 0;
      69            6 :     u64 outputOffset = 0;
      70            6 :     u32 loopIdx = 0;
      71            6 :     u64 displs[AC_MAX_RANK_NUM] = {0};
      72            6 :     u64 windowSlices = rankId_ * countLeft * unitSize_;
      73              : 
      74           54 :     for (u32 i = 0; i < ctx_->rankNum; i++) {
      75           48 :         displs[i] = strideLen * i * unitSize_;
      76              :     }
      77              : 
      78           14 :     while (countLeft > 0) {
      79            8 :         curInputPtr += inputOffset;
      80            8 :         curOutputPtr += outputOffset;
      81            8 :         u64 curCount = (countLeft > maxCountPerLoop) ? maxCountPerLoop : countLeft;
      82            8 :         u64 curSize = curCount * unitSize_; // 单位 byte
      83            8 :         windowSlices = rankId_ * curSize;
      84              : 
      85            8 :         HCCL_DEBUG("RunDeterministicReduceScatterLocal: countLeft = %llu, loop %u, curInputPtr[%p], curOutputPtr[%p],"
      86              :                    "curCount[%llu], curSize[%llu], strideLen[%llu]", countLeft,
      87              :                    loopIdx++, curInputPtr, curOutputPtr, curCount, curSize, strideLen);
      88              :         // 1. 片内数据拷贝:send->win
      89            8 :         TaskOrchestrator::SelfCpySnd2Win(curInputPtr, curSize, displs[rankId_], windowSlices, HCCL_REDUCE_RESERVED,
      90              :                                         dataType);
      91              : 
      92              :         // 2. 前同步
      93            8 :         TaskOrchestrator::DoPreSync();
      94              : 
      95              :         // 3. 跨片SDMA,send->对端win
      96            8 :         TaskOrchestrator::IpcCpySnd2Win(curInputPtr, curSize, displs, windowSlices,
      97              :                                        HCCL_REDUCE_RESERVED, dataType);
      98              : 
      99              :         // 4. 后同步
     100            8 :         TaskOrchestrator::DoPostSync();
     101              : 
     102              :         // 5. 折半计算
     103            8 :         TaskOrchestrator::SelfLocalReduce(curSize, opType, dataType);
     104              : 
     105              :         // 6. 片内数据拷贝 本端win->当前rcv buff
     106            8 :         TaskOrchestrator::SelfCpyWin2Rcv(curOutputPtr, curSize, 0, 0, HCCL_REDUCE_RESERVED,
     107              :                                         dataType);
     108              : 
     109            8 :         TaskOrchestrator::LaunchTasks();
     110              : 
     111            8 :         countLeft -= curCount;
     112            8 :         inputOffset = curSize;
     113            8 :         outputOffset = curSize;
     114              :     }
     115            6 :     return HCCL_SUCCESS;
     116              : }
     117              : 
     118            9 : HcclResult AicpuReduceScatter::RunReduceScatterWriteMode(HcclReduceOp opType, void *sendBuffer, void *recvBuffer,
     119              :     u64 dataCount, HcclDataType dataType, u64 strideLen)
     120              : {
     121            9 :     dataCount /= rankNum_;
     122              : 
     123            9 :     u64 windowSize = ctx_->windowSize;
     124            9 :     u64 maxCountPerLoop = windowSize / unitSize_; // 中转内存单次最多能够接受的output count
     125              : 
     126            9 :     uint8_t *curInputPtr = static_cast<uint8_t *>(sendBuffer);
     127            9 :     uint8_t *curOutputPtr = static_cast<uint8_t *>(recvBuffer);
     128            9 :     u64 inputOffset = 0;
     129            9 :     u64 outputOffset = 0;
     130            9 :     u64 countLeft = dataCount;
     131              : 
     132            9 :     u64 displs[AC_MAX_RANK_NUM] = {0};
     133            9 :     u64 windowOffsets[AC_MAX_RANK_NUM] = {0};
     134           81 :     for (u32 i = 0; i < rankNum_; i++) {
     135           72 :         displs[i] = i * strideLen * unitSize_;
     136           72 :         windowOffsets[i] = (i * strideLen * unitSize_) % HCCL_COPY_ALIGN;
     137              :     }
     138              : 
     139            9 :     uint32_t loopIdx = 0;
     140           17 :     while (countLeft > 0) {
     141            8 :         curInputPtr += inputOffset;
     142            8 :         curOutputPtr += outputOffset;
     143            8 :         u64 curCount = (countLeft > maxCountPerLoop) ? maxCountPerLoop : countLeft;
     144            8 :         u64 curSize = curCount * unitSize_; // 单位 byte
     145              : 
     146            8 :         HCCL_DEBUG(
     147              :             "RunReduceScatterWriteMode: loop %u, curInputPtr[%p], curOutputPtr[%p], curCount[%llu], curSize[%llu], strideLen[%llu]",
     148              :             loopIdx++, curInputPtr, curOutputPtr, curCount, curSize, strideLen);
     149              : 
     150              :         // 1. 片内数据 snd->win
     151            8 :         CHK_RET(TaskOrchestrator::SelfCpySnd2Win(curInputPtr, curSize, displs[rankId_], windowOffsets[rankId_],
     152              :             HCCL_REDUCE_RESERVED, dataType)); // 1, 0
     153              :         // 2. 前同步
     154            8 :         CHK_RET(TaskOrchestrator::DoPreSync()); // 15 sqe, 35
     155              : 
     156              :         // 3. 跨片SDMA send->其他window
     157            8 :         CHK_RET(TaskOrchestrator::IpcCpySnd2Win(curInputPtr, curSize, displs, windowOffsets, opType,
     158              :             dataType)); // 0, 7
     159              : 
     160              :         // 4. 后同步
     161            8 :         CHK_RET(TaskOrchestrator::DoPostSync()); // 8, 21
     162              : 
     163              :         // 5. 片内数据 拷贝到recv
     164            8 :         CHK_RET(TaskOrchestrator::SelfCpyWin2Rcv(curOutputPtr, curSize, windowOffsets[rankId_], 0, HCCL_REDUCE_RESERVED,
     165              :             dataType)); // 1, 0
     166              : 
     167            8 :         CHK_RET(TaskOrchestrator::LaunchTasks()); // 25, 63
     168              : 
     169            8 :         countLeft -= curCount;
     170            8 :         inputOffset = curSize;
     171            8 :         outputOffset = curSize;
     172              :     }
     173              : 
     174            9 :     return HCCL_SUCCESS;
     175              : }
     176              : 
     177            0 : HcclResult AicpuReduceScatter::RunReduceScatterReadMode(HcclReduceOp opType, void *sendBuffer, void *recvBuffer,
     178              :     u64 dataCount, HcclDataType dataType)
     179              : {
     180            0 :     uint32_t dataSize = dataCount * unitSize_; // 输入大小
     181            0 :     u64 scatterSize = dataSize / rankNum_;     // 输出大小
     182              : 
     183            0 :     HCCL_DEBUG("RunReduceScatterReadMode: sendBuffer[%p], recvBuffer[%p], dataCount[%llu], dataSize[%llu]", sendBuffer,
     184              :         recvBuffer, dataCount, dataSize);
     185              : 
     186              :     // 1. 片内数据 snd->win 一次性全拷贝
     187            0 :     CHK_RET(TaskOrchestrator::SelfCpySnd2Win(sendBuffer, dataSize, 0, 0, HCCL_REDUCE_RESERVED, dataType));
     188              :     // 2. 前同步
     189            0 :     CHK_RET(TaskOrchestrator::DoPreSync()); // 15 sqe, 35
     190              : 
     191              :     // 3. 片内win->recv   实测放在和跨片sdma并行时性能更好
     192            0 :     CHK_RET(TaskOrchestrator::SelfCpyWin2Rcv(recvBuffer, scatterSize, rankId_ * scatterSize, 0, HCCL_REDUCE_RESERVED,
     193              :         dataType));
     194              : 
     195              :     // 3. 跨片SDMA 其他win->recv
     196            0 :     u64 winOffsets[AC_MAX_RANK_NUM] = {0};
     197            0 :     for (size_t i = 0; i < rankNum_; i++) {
     198            0 :         winOffsets[i] = rankId_ * scatterSize;
     199              :     }
     200            0 :     CHK_RET(TaskOrchestrator::IpcCpyWin2Rcv(recvBuffer, scatterSize, winOffsets, nullptr, opType, dataType));
     201              : 
     202              :     // 4. 后同步
     203            0 :     CHK_RET(TaskOrchestrator::DoPostSync()); // 8 sqe, 21
     204              : 
     205            0 :     CHK_RET(TaskOrchestrator::LaunchTasks()); // 25, 63
     206              : 
     207            0 :     return HCCL_SUCCESS;
     208              : }
     209              : 
     210            0 : std::vector<Slice> AicpuReduceScatter::PrepareMeshSlice(u64 dataSize, uint32_t rankNum_)
     211              : {
     212            0 :     std::vector<Slice> meshSlices;
     213            0 :     uint32_t roundCnt = rankNum_ - 1;
     214              : 
     215            0 :     u64 sizePerRound = AlignUp<u64>((dataSize + roundCnt - 1) / roundCnt, HCCL_MIN_SLICE_ALIGN);
     216            0 :     int64_t rankResidueSize = static_cast<int64_t>(dataSize);
     217              : 
     218            0 :     while (rankResidueSize > 0) {
     219            0 :         Slice singleRoundSlice;
     220            0 :         singleRoundSlice.offset = dataSize - rankResidueSize;
     221            0 :         singleRoundSlice.size = std::min<u64>(sizePerRound, rankResidueSize);
     222            0 :         rankResidueSize -= singleRoundSlice.size;
     223            0 :         meshSlices.push_back(singleRoundSlice);
     224              :     }
     225              : 
     226            0 :     for (const auto &slice : meshSlices) {
     227            0 :         HCCL_DEBUG("Slice offset:%lu, size:%lu", slice.offset, slice.size);
     228              :     }
     229            0 :     return meshSlices;
     230            0 : }
     231              : 
     232            0 : HcclResult AicpuReduceScatter::RunDeterministicReduceScatter(HcclReduceOp opType, void *sendBuffer,
     233              :     void *recvBuffer, u64 dataCount, HcclDataType dataType, u64 strideCount)
     234              : {
     235            0 :     HCCL_INFO("RunDeterministicReduceScatter strideCount:%u", strideCount);
     236            0 :     dataCount /= rankNum_;
     237              : 
     238            0 :     u64 maxCountPerLoop = ctx_->windowSize / (rankNum_ * unitSize_); // 中转内存单次最多能够接受的output count
     239            0 :     u64 curCount = 0;
     240            0 :     uint8_t *curInputPtr = static_cast<uint8_t *>(sendBuffer);
     241            0 :     uint8_t *curOutputPtr = static_cast<uint8_t *>(recvBuffer);
     242            0 :     for (u64 countLeft = dataCount, inputOffset = 0, outputOffset = 0; countLeft > 0; countLeft -= curCount) {
     243            0 :         curInputPtr += inputOffset;
     244            0 :         curOutputPtr += outputOffset;
     245            0 :         curCount = ((countLeft * unitSize_ * rankNum_) > ctx_->windowSize) ? maxCountPerLoop : countLeft;
     246            0 :         u64 curSize = curCount * unitSize_;
     247              : 
     248            0 :         CHK_RET(RunCurrentDeterministicReduceScatter(opType, curInputPtr, curOutputPtr, strideCount * unitSize_,
     249              :             curSize, dataType));
     250              : 
     251            0 :         inputOffset = curSize;
     252            0 :         outputOffset = curSize;
     253              :     }
     254              : 
     255            0 :     return HCCL_SUCCESS;
     256              : }
     257              : 
     258            0 : HcclResult AicpuReduceScatter::RunCurrentDeterministicReduceScatter(HcclReduceOp opType, uint8_t *curInputPtr,
     259              :     uint8_t *curOutputPtr, u64 strideSize, u64 curSize, HcclDataType dataType)
     260              : {
     261            0 :     std::vector<Slice> slices = PrepareMeshSlice(curSize, rankNum_);
     262            0 :     std::vector<uint32_t> srcRankOrder;
     263            0 :     std::vector<uint32_t> dstRankOrder;
     264            0 :     for (uint32_t i = 1; i <= rankNum_ - 1; i++) {
     265            0 :         srcRankOrder.push_back((rankId_ + rankNum_ - i) % rankNum_);
     266            0 :         dstRankOrder.push_back((rankId_ + i) % rankNum_);
     267            0 :         HCCL_DEBUG("SrcRank:%u dstRank:%u", (rankId_ + rankNum_ - i) % rankNum_, (rankId_ + i) % rankNum_);
     268              :     }
     269              :     // 1. 片内send->recv
     270            0 :     AicpuDispatcher::CopyData(rankId_, static_cast<void *>(curInputPtr + rankId_ * strideSize), curOutputPtr, curSize,
     271              :         dataType, HCCL_REDUCE_RESERVED, rankId_);
     272            0 :     for (uint32_t round = 0; round < rankNum_ - 1; round++) {
     273              :         // 2. 片内send->win
     274            0 :         for (size_t i = 0; i < slices.size(); i++) {
     275            0 :             uint32_t idx = (round + i) % (rankNum_ - 1);
     276            0 :             u64 sendOff = dstRankOrder[idx] * strideSize + slices[i].offset;
     277            0 :             HCCL_DEBUG("Cpy send to win, dstRank:%u, srcOffset:%lu, dstOffset:%lu, size:%lu", dstRankOrder[idx],
     278              :                 sendOff, slices[i].offset, slices[i].size);
     279            0 :             CHK_RET(TaskOrchestrator::SelfCpySnd2Win(curInputPtr, slices[i].size, sendOff, slices[i].offset,
     280              :                 HCCL_REDUCE_RESERVED, dataType));
     281              :         }
     282              :         // 3. 前同步
     283            0 :         CHK_RET(TaskOrchestrator::DoPreSync());
     284              :         // 4. 跨片读 inline reduce
     285            0 :         for (size_t i = 0; i < slices.size(); i++) {
     286            0 :             uint32_t idx = (round + i) % (rankNum_ - 1);
     287            0 :             HCCL_DEBUG("Ipc read win to rcv, srcRank:%u, srcOffset:%lu, dstOffset:%lu, size:%lu", srcRankOrder[idx],
     288              :                 slices[i].offset, slices[i].offset, slices[i].size);
     289            0 :             CHK_RET(TaskOrchestrator::IpcCpyWin2RcvP2P(curOutputPtr, srcRankOrder[idx], slices[i].size,
     290              :                 slices[i].offset, slices[i].offset, opType, dataType));
     291              :         }
     292              :         // 5. 后同步
     293            0 :         CHK_RET(TaskOrchestrator::DoPostSync());
     294              :     }
     295            0 :     CHK_RET(TaskOrchestrator::LaunchTasks());
     296            0 :     return HCCL_SUCCESS;
     297            0 : }
     298              : 
     299           28 : HcclResult AicpuReduceScatter::GenRingTask(HcclReduceOp opType, u64 sndAddr, u64 rcvAddr, u64 curSize,
     300              :     u64 scatterSize, HcclDataType dataType, uint32_t streamId, bool isClockwise, uint32_t step) const
     301              : {
     302           28 :     const u64 winIn = ctx_->rankInfo[rankId_].window + (isClockwise ? 0U : ctx_->windowSize / RING_NUM);
     303           28 :     const u64 winOut = ctx_->rankInfo[rankId_].windowOut + (isClockwise ? 0U : ctx_->windowSize / RING_NUM);
     304           28 :     const uint32_t preRankId = isClockwise ? (rankId_ + rankNum_ - 1U) % rankNum_ : (rankId_ + 1) % rankNum_;
     305           28 :     const uint32_t postRankId = isClockwise ? (rankId_ + 1) % rankNum_ : (rankId_ + rankNum_ - 1U) % rankNum_;
     306           28 :     const u64 preWinIn = ctx_->rankInfo[preRankId].window + (isClockwise ? 0U : ctx_->windowSize / RING_NUM);
     307           28 :     const u64 preWinOut = ctx_->rankInfo[preRankId].windowOut + (isClockwise ? 0U : ctx_->windowSize / RING_NUM);
     308           28 :     const bool evenStep = (step % 2U == 0U); // 2: 环上winIn winOut轮流接收, 奇数轮in->out, 偶数轮out->in
     309              : 
     310           28 :     HcclResult ret = HCCL_SUCCESS;
     311           28 :     if (step == 1U) { // 首轮send->winIn winOut
     312            4 :         ret = AicpuDispatcher::CopyData(streamId, sndAddr, winIn, curSize, dataType, HCCL_REDUCE_RESERVED, rankId_);
     313            4 :         CHK_PRT_RET(ret != HCCL_SUCCESS, HCCL_ERROR("Turn %u step %u send to clock winIn failed", turn_, step), ret);
     314              : 
     315            8 :         ret = AicpuDispatcher::CopyData(streamId, sndAddr + scatterSize, winOut, curSize, dataType,
     316            4 :             HCCL_REDUCE_RESERVED, rankId_);
     317            4 :         CHK_PRT_RET(ret != HCCL_SUCCESS, HCCL_ERROR("Turn %u step %u send to clock winOut failed", turn_, step), ret);
     318              :     } else {
     319           24 :         ret = AicpuDispatcher::CopyData(streamId, sndAddr, evenStep ? winIn : winOut, curSize, dataType,
     320           24 :             HCCL_REDUCE_RESERVED, rankId_);
     321           24 :         CHK_PRT_RET(ret != HCCL_SUCCESS, HCCL_ERROR("Turn %u step %u send to clock winOut failed", turn_, step), ret);
     322              :     }
     323              :     // 片间同步 notify后卡 wait前卡
     324           28 :     ret = AicpuDispatcher::SignalRecord(streamId, postRankId, AicpuDispatcher::IPC, AicpuDispatcher::PRE_SYNC);
     325           28 :     CHK_PRT_RET(ret != HCCL_SUCCESS, HCCL_ERROR("Turn %u step %u notify post rank failed", turn_, step), ret);
     326              : 
     327           28 :     ret = AicpuDispatcher::SignalWait(streamId, preRankId, AicpuDispatcher::IPC, AicpuDispatcher::PRE_SYNC);
     328           28 :     CHK_PRT_RET(ret != HCCL_SUCCESS, HCCL_ERROR("Turn %u step %u wait pre rank failed", turn_, step), ret);
     329              : 
     330              :     // 片间memcpy 前卡window->window
     331           28 :     ret = AicpuDispatcher::CopyData(streamId, evenStep ? preWinOut : preWinIn, evenStep ? winIn : winOut, curSize,
     332              :         dataType, opType, preRankId);
     333           28 :     CHK_PRT_RET(ret != HCCL_SUCCESS, HCCL_ERROR("Turn %u step %u cpy pre window failed", turn_, step), ret);
     334           28 :     if (step == rankNum_ - 1U) { // 最后一轮 win输出至recv
     335            4 :         ret = AicpuDispatcher::CopyData(streamId, evenStep ? winIn : winOut, rcvAddr, curSize, dataType,
     336            4 :             HCCL_REDUCE_RESERVED, rankId_);
     337            4 :         CHK_PRT_RET(ret != HCCL_SUCCESS, HCCL_ERROR("Turn %u step %u window to recv failed", turn_, step), ret);
     338              :     }
     339           28 :     ret = AicpuDispatcher::SignalRecord(streamId, preRankId, AicpuDispatcher::IPC, AicpuDispatcher::POST_SYNC);
     340           28 :     CHK_PRT_RET(ret != HCCL_SUCCESS, HCCL_ERROR("Turn %u step %u notify pre rank failed", turn_, step), ret);
     341              :     // 片间同步 notify前卡 wait后卡
     342           28 :     ret = AicpuDispatcher::SignalWait(streamId, postRankId, AicpuDispatcher::IPC, AicpuDispatcher::POST_SYNC);
     343           28 :     CHK_PRT_RET(ret != HCCL_SUCCESS, HCCL_ERROR("Turn %u step %u wait post rank failed", turn_, step), ret);
     344           28 :     return HCCL_SUCCESS;
     345              : }
     346              : 
     347            2 : HcclResult AicpuReduceScatter::RunDoubleRingReduceScatter(HcclReduceOp opType, u64 sendBuffer, u64 recvBuffer,
     348              :     u64 dataCount, HcclDataType dataType) const
     349              : {
     350            2 :     const u64 scatterSize = dataCount / rankNum_ * unitSize_;
     351            2 :     if (scatterSize > ctx_->windowSize / RING_NUM) {
     352            0 :         HCCL_INFO("Tile scatter size %lu max less than window size %lu/RING_NUM", scatterSize, ctx_->windowSize);
     353              :     }
     354              :     thread_local static u64 sndAddr[RING_NUM] = { 0UL };
     355              :     thread_local static u64 rcvAddr[RING_NUM] = { 0UL };
     356            2 :     if (turn_ == 0U) { // 首个tile时初始化snd rcv地址
     357            1 :         sndAddr[0] = sendBuffer;
     358            1 :         rcvAddr[0] = recvBuffer;
     359            1 :         rcvAddr[1] = recvBuffer + ctx_->totalCnt * unitSize_ / rankNum_;
     360              :     }
     361            2 :     sndAddr[1] = sndAddr[0] + 2U * scatterSize + scatterSize; // 2 首轮需要同时输出到winIn和winOut
     362              : 
     363            2 :     HCCL_INFO("DR reducescatter snd addr %p %p, rcv addr %p %p, size %lu", sndAddr[0], sndAddr[1], rcvAddr[0],
     364              :         rcvAddr[1], scatterSize);
     365            2 :     uint32_t mainStream = rankId_;
     366            2 :     uint32_t subStream = (rankId_ + 1U) % rankNum_;
     367            2 :     HcclResult ret = HCCL_SUCCESS;
     368              : 
     369            2 :     u64 maxCountPerLoop = ctx_->windowSize / RING_NUM / unitSize_; // 中转内存单次最多能够接受的output count
     370            2 :     u64 countLeft = dataCount / rankNum_;
     371            2 :     bool isWindowFirst = true;
     372            2 :     bool isWindowLast = false;
     373              :     // windowSize循环
     374            2 :     u32 loopIdx = 0;
     375            4 :     while (countLeft > 0) {
     376            2 :         u64 curCount = (countLeft > maxCountPerLoop) ? maxCountPerLoop : countLeft;
     377            2 :         u64 curSize = curCount * unitSize_; // 单位 byte
     378              : 
     379            2 :         HCCL_DEBUG("DR reducescatter: loop %u, snd addr[%p %p], rcv addr[%p %p], curCount[%llu], curSize[%llu]",
     380              :             loopIdx++, sndAddr[0], sndAddr[1], rcvAddr[0], rcvAddr[1], curCount, curSize);
     381              : 
     382            2 :         sndAddr[1] -= curSize; // 逆时针输入在执行前向上偏移
     383            2 :         rcvAddr[1] -= curSize; // 逆时针输出在执行前向上偏移
     384              : 
     385            2 :         isWindowLast = ((countLeft - curCount) == 0);
     386              : 
     387            2 :         u64 stepSndAddrClockwise = sndAddr[0];
     388            2 :         u64 stepSndAddrAnticlockwise = sndAddr[1];
     389           16 :         for (uint32_t step = 1U; step <= rankNum_ - 1U; step++) {
     390           14 :             if (isWindowFirst) {
     391              :                 // ccore wait
     392           14 :                 u64 waitAddr = ctx_->workSpaceAddr + ctx_->notifyOff + offsetof(AivAicpuOpParam, sendCnt);
     393           14 :                 ret = AicpuDispatcher::AddCcoreWait(mainStream, waitAddr, turn_ * (rankNum_ - 1U) + step,
     394           14 :                     (turn_ + 1U >= ctx_->totalTurnCnt) && (step == rankNum_ - 1U));
     395           14 :                 CHK_PRT_RET(ret != HCCL_SUCCESS, HCCL_ERROR("Turn %u step %u add ccore wait failed", turn_, step), ret);
     396              :             }
     397              : 
     398              :             // 主->从
     399           14 :             CHK_RET(
     400              :                 AicpuDispatcher::SignalRecord(mainStream, subStream, AicpuDispatcher::NO_IPC, AicpuDispatcher::PRE_SYNC));
     401           14 :             CHK_RET(AicpuDispatcher::SignalWait(subStream, subStream, AicpuDispatcher::NO_IPC, AicpuDispatcher::PRE_SYNC));
     402              : 
     403              :             // 顺时针环
     404           14 :             CHK_RET(GenRingTask(opType, stepSndAddrClockwise, rcvAddr[0], curSize, scatterSize, dataType, mainStream, true, step));
     405           14 :             stepSndAddrClockwise += ((step == 1U) ? 2U * scatterSize * RING_NUM : scatterSize * RING_NUM); // 2 首轮需要同时输出到winIn和winOut
     406              : 
     407              :             // 逆时针环
     408           14 :             CHK_RET(GenRingTask(opType, stepSndAddrAnticlockwise, rcvAddr[1], curSize, scatterSize, dataType, subStream, false, step));
     409           14 :             stepSndAddrAnticlockwise += ((step == 1U) ? 2U * scatterSize + scatterSize : scatterSize * RING_NUM); // 2 首轮需要同时输出到winIn和winOut
     410              : 
     411              :             // 从->主
     412           14 :             CHK_RET(
     413              :                 AicpuDispatcher::SignalRecord(subStream, subStream, AicpuDispatcher::NO_IPC, AicpuDispatcher::POST_SYNC));
     414           14 :             CHK_RET(
     415              :                 AicpuDispatcher::SignalWait(mainStream, subStream, AicpuDispatcher::NO_IPC, AicpuDispatcher::POST_SYNC));
     416           14 :             if (isWindowLast) {
     417           14 :                 ret = TaskOrchestrator::AddBarrier(mainStream, rankId_, rankNum_);
     418           14 :                 CHK_PRT_RET(ret != HCCL_SUCCESS, HCCL_ERROR("Turn %u step %u add barrier failed", turn_, step), ret);
     419              :                 // ccore notify
     420           14 :                 ret = AicpuDispatcher::AddCcoreNotify(mainStream, turn_ * (rankNum_ - 1U) + step);
     421           14 :                 CHK_PRT_RET(ret != HCCL_SUCCESS, HCCL_ERROR("Turn %u step %u add ccore notify failed", turn_, step), ret);
     422              :             }
     423              :         }
     424            2 :         isWindowFirst = false;
     425            2 :         countLeft -= curCount;
     426            2 :         sndAddr[0] += curSize; // 顺时针输入向下偏移
     427            2 :         rcvAddr[0] += curSize; // 顺时针输出向下偏移
     428              :     }
     429              : 
     430              :     // tile之间回退一个scatterSize再偏移
     431            2 :     sndAddr[0] = sndAddr[0] - scatterSize + rankNum_ * RING_NUM * scatterSize;
     432              : 
     433            2 :     ret = TaskOrchestrator::LaunchTasks();
     434            2 :     CHK_PRT_RET(ret != HCCL_SUCCESS, HCCL_ERROR("Launch tasks failed"), ret);
     435            2 :     return HCCL_SUCCESS;
     436              : }
     437              : 
     438            2 : HcclResult AicpuReduceScatter::RunSwitchReduceScatter(HcclReduceOp opType, u64 sendBuffer, u64 recvBuffer,
     439              :     u64 dataCount, HcclDataType dataType) const
     440              : {
     441            2 :     const u64 scatterSize = dataCount / rankNum_ * unitSize_;
     442            2 :     if (scatterSize > ctx_->windowSize) {
     443            0 :         HCCL_INFO("Tile scatter size %lu max less than window size %lu", scatterSize, ctx_->windowSize);
     444              :     }
     445              :     thread_local static u64 sndAddr = 0UL;
     446              :     thread_local static u64 rcvAddr = 0UL;
     447            2 :     if (turn_ == 0U) { // 首个tile时初始化snd rcv地址
     448            1 :         sndAddr = sendBuffer;
     449            1 :         rcvAddr = recvBuffer;
     450              :     }
     451            2 :     uint32_t mainStream = rankId_;
     452            2 :     HCCL_INFO("SW reducescatter snd addr %p, rcv addr %p, size %lu", sndAddr, rcvAddr, scatterSize);
     453              : 
     454            2 :     HcclResult ret = HCCL_SUCCESS;
     455            2 :     u64 maxCountPerLoop = ctx_->windowSize / unitSize_; // 中转内存单次最多能够接受的output count
     456            2 :     u64 countLeft = dataCount / rankNum_;
     457            2 :     bool isWindowFirst = true;
     458            2 :     bool isWindowLast = false;
     459              :     // windowSize循环
     460            4 :     while (countLeft > 0) {
     461            2 :         u64 curCount = (countLeft > maxCountPerLoop) ? maxCountPerLoop : countLeft;
     462            2 :         u64 curSize = curCount * unitSize_; // 单位 byte
     463              : 
     464            2 :         isWindowLast = ((countLeft - curCount) == 0);
     465              : 
     466            2 :         u64 stepSndAddr = sndAddr;
     467           18 :         for (uint32_t step = 1U; step <= rankNum_; step++) {
     468           16 :             uint32_t preRankId = (rankId_ + rankNum_ - step) % rankNum_;
     469           16 :             uint32_t postRankId = (rankId_ + step) % rankNum_;
     470           16 :             if (isWindowFirst) {
     471              :                 // ccore wait
     472           16 :                 u64 waitAddr = ctx_->workSpaceAddr + ctx_->notifyOff + offsetof(AivAicpuOpParam, sendCnt);
     473           16 :                 ret = AicpuDispatcher::AddCcoreWait(mainStream, waitAddr, turn_ * rankNum_ + step,
     474           16 :                     (turn_ + 1u >= ctx_->totalTurnCnt) && (step == rankNum_));
     475           16 :                 CHK_PRT_RET(ret != HCCL_SUCCESS, HCCL_ERROR("Turn %u step %u add ccore wait failed", turn_, step), ret);
     476              :             }
     477              : 
     478           16 :             if (step != rankNum_) {
     479              :                 // 片间同步 notify前卡 wait后卡
     480           14 :                 ret = AicpuDispatcher::SignalRecord(mainStream, preRankId, AicpuDispatcher::IPC, AicpuDispatcher::PRE_SYNC);
     481           14 :                 CHK_PRT_RET(ret != HCCL_SUCCESS, HCCL_ERROR("Turn %u step %u notify pre rank failed", turn_, step), ret);
     482              : 
     483           14 :                 ret = AicpuDispatcher::SignalWait(mainStream, postRankId, AicpuDispatcher::IPC, AicpuDispatcher::PRE_SYNC);
     484           14 :                 CHK_PRT_RET(ret != HCCL_SUCCESS, HCCL_ERROR("Turn %u step %u wait post rank failed", turn_, step), ret);
     485              : 
     486              :                 // 片间memcpy snd->后序卡win 首轮采用覆盖
     487           14 :                 ret = AicpuDispatcher::CopyData(mainStream, stepSndAddr, ctx_->rankInfo[postRankId].window, curSize,
     488              :                     dataType, step == 1U ? HCCL_REDUCE_RESERVED : opType, postRankId);
     489           14 :                 CHK_PRT_RET(ret != HCCL_SUCCESS, HCCL_ERROR("Turn %u step %u cpy snd to post win failed", turn_, step),
     490              :                     ret);
     491           14 :                 stepSndAddr += scatterSize;
     492              : 
     493              :                 // 片间同步 notify后卡 wait前卡
     494              :                 ret =
     495           14 :                     AicpuDispatcher::SignalRecord(mainStream, postRankId, AicpuDispatcher::IPC, AicpuDispatcher::POST_SYNC);
     496           14 :                 CHK_PRT_RET(ret != HCCL_SUCCESS, HCCL_ERROR("Turn %u step %u notify post rank failed", turn_, step), ret);
     497              : 
     498           14 :                 ret = AicpuDispatcher::SignalWait(mainStream, preRankId, AicpuDispatcher::IPC, AicpuDispatcher::POST_SYNC);
     499           14 :                 CHK_PRT_RET(ret != HCCL_SUCCESS, HCCL_ERROR("Turn %u step %u wait pre rank failed", turn_, step), ret);
     500              :             } else { // 最后一轮仅作本卡内拷贝
     501              :                 // snd->recv
     502            4 :                 ret = AicpuDispatcher::CopyData(mainStream, stepSndAddr, rcvAddr, curSize, dataType,
     503            2 :                                                 HCCL_REDUCE_RESERVED, rankId_);
     504            2 :                 CHK_PRT_RET(ret != HCCL_SUCCESS, HCCL_ERROR("Turn %u step %u cpy snd to rcv failed", turn_, step), ret);
     505            2 :                 stepSndAddr += scatterSize;
     506              : 
     507              :                 // win->recv
     508            4 :                 ret = AicpuDispatcher::CopyData(mainStream, ctx_->rankInfo[rankId_].window, rcvAddr, curSize, dataType,
     509            2 :                     opType, rankId_);
     510            2 :                 CHK_PRT_RET(ret != HCCL_SUCCESS, HCCL_ERROR("Turn %u step %u cpy win to rcv failed", turn_, step), ret);
     511              :             }
     512           16 :             if (isWindowLast) {
     513           16 :                 ret = TaskOrchestrator::AddBarrier(mainStream, rankId_, rankNum_);
     514           16 :                 CHK_PRT_RET(ret != HCCL_SUCCESS, HCCL_ERROR("Turn %u step %u add barrier failed", turn_, step), ret);
     515              : 
     516              :                 // ccore notify
     517           16 :                 ret = AicpuDispatcher::AddCcoreNotify(mainStream, turn_ * rankNum_ + step);
     518           16 :                 CHK_PRT_RET(ret != HCCL_SUCCESS, HCCL_ERROR("Turn %u step %u add ccore notify failed", turn_, step), ret);
     519              :             }
     520              :         }
     521            2 :         isWindowFirst = false;
     522            2 :         countLeft -= curCount;
     523            2 :         sndAddr += curSize;
     524            2 :         rcvAddr += curSize;
     525              :     }
     526              : 
     527              :     // 回退一个scatterSize再偏移
     528            2 :     sndAddr = sndAddr - scatterSize + rankNum_ * scatterSize;
     529              : 
     530            2 :     ret = TaskOrchestrator::LaunchTasks();
     531            2 :     CHK_PRT_RET(ret != HCCL_SUCCESS, HCCL_ERROR("Launch tasks failed"), ret);
     532            2 :     return HCCL_SUCCESS;
     533              : }
        

Generated by: LCOV version 2.0-1