LCOV - code coverage report
Current view: top level - legacy/ascend910/algorithm/base/alg_template/temp_alltoallv - alltoallv_continuous_pipeline.cc (source / functions) Coverage Total Hit
Test: coverage.info Lines: 0.0 % 544 0
Test Date: 2026-08-04 10:52:23 Functions: 0.0 % 40 0

            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 "alltoallv_continuous_pipeline.h"
      12              : 
      13              : #include <vector>
      14              : #include <algorithm>
      15              : #include "alg_template_register.h"
      16              : 
      17              : namespace hccl {
      18            0 : AlltoallvContinuousPipeline::AlltoallvContinuousPipeline(const HcclDispatcher dispatcher)
      19            0 :     : AlgTemplateBase(dispatcher)
      20            0 : {}
      21              : 
      22            0 : AlltoallvContinuousPipeline::~AlltoallvContinuousPipeline() {}
      23              : 
      24            0 : HcclResult AlltoallvContinuousPipeline::PrepareSendRecvInfo( std::vector<SendRecvInfo> &sendRecvInfoList)
      25              : {
      26            0 :     if (sendRecvInfoList.size() == 1) {
      27              :         // 真实业务场景
      28            0 :         SendRecvInfo &localSendRecvInfo = sendRecvInfoList[0];
      29            0 :         localSendCounts_ = std::move(localSendRecvInfo.sendCounts);
      30            0 :         localSendDispls_ = std::move(localSendRecvInfo.sendDispls);
      31            0 :         localRecvCounts_ = std::move(localSendRecvInfo.recvCounts);
      32            0 :         localRecvDispls_ = std::move(localSendRecvInfo.recvDispls);
      33            0 :         needCollectInfo_ = true; // 需要收集信息
      34            0 :         std::copy(localRecvCounts_.begin(), localRecvCounts_.end(), intraRecvCounts_[intraRankId_].begin());
      35              :     } else {
      36              :         // 适配算法分析器,实际业务不会走这个分支
      37            0 :         SendRecvInfo &localSendRecvInfo = sendRecvInfoList[userRank_];
      38              : 
      39            0 :         std::copy(localSendRecvInfo.sendCounts.begin(),
      40              :             localSendRecvInfo.sendCounts.end(),
      41            0 :             std::back_inserter(localSendCounts_));
      42            0 :         std::copy(localSendRecvInfo.sendDispls.begin(),
      43              :             localSendRecvInfo.sendDispls.end(),
      44            0 :             std::back_inserter(localSendDispls_));
      45            0 :         std::copy(localSendRecvInfo.recvCounts.begin(),
      46              :             localSendRecvInfo.recvCounts.end(),
      47            0 :             std::back_inserter(localRecvCounts_));
      48            0 :         std::copy(localSendRecvInfo.recvDispls.begin(),
      49              :             localSendRecvInfo.recvDispls.end(),
      50            0 :             std::back_inserter(localRecvDispls_));
      51              : 
      52            0 :         for (u32 intraRankIdx = 0; intraRankIdx < intraRankSize_; ++intraRankIdx) {
      53            0 :             const u32 remoteRank = interRankId_ * intraRankSize_ + intraRankIdx;
      54            0 :             SendRecvInfo &sendRecvInfo = sendRecvInfoList[remoteRank];
      55            0 :             std::copy(
      56            0 :                 sendRecvInfo.recvCounts.begin(), sendRecvInfo.recvCounts.end(), intraRecvCounts_[intraRankIdx].begin());
      57              :         }
      58            0 :         needCollectInfo_ = false;
      59              :     }
      60              : 
      61            0 :     intraLoopNum_ = GetLocalLoopNum();
      62            0 :     return HCCL_SUCCESS;
      63              : }
      64              : 
      65            0 : HcclResult AlltoallvContinuousPipeline::PrepareTopoInfo(const u32 userRank, const SubCommInfo &level0CommInfo,
      66              :     const SubCommInfo &level1CommInfo)
      67              : {
      68            0 :     constexpr u32 MIN_RANKSIZE = 2;
      69            0 :     interRankSize_ = level1CommInfo.localRankSize;
      70            0 :     CHK_PRT_RET(interRankSize_ < MIN_RANKSIZE,
      71              :         HCCL_ERROR("[AlltoallvContinuousPipeline][PrepareTopoInfo] Unexpected inter rank size[%u], which should >= 2.",
      72              :             interRankSize_),
      73              :         HCCL_E_PARA);
      74              : 
      75            0 :     intraRankSize_ = level0CommInfo.localRankSize;
      76            0 :     CHK_PRT_RET(intraRankSize_ < MIN_RANKSIZE,
      77              :         HCCL_ERROR("[AlltoallvContinuousPipeline][PrepareTopoInfo] Unexpected intra rank size[%u], which should >= 2.",
      78              :             intraRankSize_),
      79              :         HCCL_E_PARA);
      80              : 
      81            0 :     userRankSize_ = intraRankSize_ * interRankSize_;
      82            0 :     userRank_ = userRank;
      83            0 :     interRankId_ = level1CommInfo.localRank;
      84            0 :     intraRankId_ = level0CommInfo.localRank;
      85            0 :     HCCL_INFO("[AlltoallvContinuousPipeline][PrepareTopoInfo] userRank[%u], intraRankId[%u], intraRankSize[%u], "
      86              :         "interRankId[%u], interRankSize[%u]", userRank_, intraRankId_, intraRankSize_, interRankId_, interRankSize_);
      87              : 
      88              :     // 按照module将rank分组
      89            0 :     ranksPerModule_.resize(interRankSize_);
      90            0 :     for (u32 interRank = 0; interRank < interRankSize_; ++interRank) {
      91            0 :         ranksPerModule_[interRank].resize(intraRankSize_);
      92            0 :         for (u32 intraRank = 0; intraRank < intraRankSize_; ++intraRank) {
      93            0 :             ranksPerModule_[interRank][intraRank] = interRank * intraRankSize_ + intraRank;
      94              :         }
      95              :     }
      96            0 :     return HCCL_SUCCESS;
      97              : }
      98              : 
      99            0 : HcclResult AlltoallvContinuousPipeline::Prepare(const u32 userRank, const A2aPipelineMemory &a2aPipelineMemory,
     100              :     const SubCommInfo &level0CommInfo, const SubCommInfo &level1CommInfo,
     101              :     const Stream &mainStream, std::vector<Stream> &subStream,
     102              :     std::vector<std::shared_ptr<LocalNotify>> &notifyMain, std::vector<std::shared_ptr<LocalNotify>> &notifySub,
     103              :     std::vector<SendRecvInfo> &sendRecvInfoList, const HcclDataType dataType,
     104              :     const HcclWorkflowMode workMode)
     105              : {
     106              :     // 运行模式:当前只支持单算子
     107            0 :     workMode_ = workMode;
     108            0 :     CHK_PRT_RET(workMode_ != HcclWorkflowMode::HCCL_WORKFLOW_MODE_OP_BASE,
     109              :         HCCL_ERROR("[AlltoallvContinuousPipeline] This template support opbase mode only."),
     110              :         HCCL_E_INTERNAL);
     111              : 
     112              :     // 拓扑信息
     113            0 :     CHK_RET(PrepareTopoInfo(userRank, level0CommInfo, level1CommInfo));
     114              : 
     115              :     // 并发度暂定为1 - 不并发
     116            0 :     rdmaConcurrentNum_ = 1;
     117              : 
     118              :     // 数据类型
     119            0 :     dataType_ = dataType;
     120            0 :     unitSize_ = DataUnitSize(dataType_);
     121              : 
     122              :     // 内存
     123            0 :     inputMem_ = a2aPipelineMemory.userInput;
     124            0 :     outputMem_ = a2aPipelineMemory.userOutput;
     125            0 :     inBuffer_ = a2aPipelineMemory.cclInBuffer;
     126            0 :     outBuffer_ = a2aPipelineMemory.cclOutBuffer;
     127              : 
     128            0 :     flagAreaRefreshData_.resize(userRankSize_);
     129              : 
     130              :     // server内其他卡的recv counts
     131            0 :     intraRecvCounts_.resize(intraRankSize_);
     132            0 :     for (auto &countVec : intraRecvCounts_) {
     133            0 :         countVec.resize(userRankSize_);
     134              :     }
     135              : 
     136              :     // 流和notify
     137            0 :     mainStream_ = mainStream;
     138            0 :     CHK_RET(PartitionSubStreamsAndNotifies(subStream, notifyMain, notifySub));
     139              : 
     140              :     // 链路
     141            0 :     intraLinks_ = level0CommInfo.links;
     142            0 :     interLinks_ = level1CommInfo.links;
     143            0 :     HCCL_INFO("[AlltoallvContinuousPipeline][Prepare] Link info: interLinksNum[%u], intraLinksNum[%u]",
     144              :         interLinks_.size(), intraLinks_.size());
     145              : 
     146              :     // pingpong模式: module间只有1步:双module
     147            0 :     enablePingPong_ = rdmaConcurrentNum_ >= interRankSize_ - 1;
     148              :     // 切分buffer
     149            0 :     CHK_RET(SplitBuffer(enablePingPong_));
     150              : 
     151              :     // 收发信息
     152            0 :     CHK_RET(PrepareSendRecvInfo(sendRecvInfoList));
     153              :     
     154              : 
     155            0 :     return HCCL_SUCCESS;
     156              : }
     157              : 
     158            0 : HcclResult AlltoallvContinuousPipeline::SplitBuffer(const bool enablePingPong)
     159              : {
     160              :     // 单个counts数组的大小
     161            0 :     const u64 singleRankCountsInfoSize = sizeof(u64) * userRankSize_;
     162              :     // 全局counts的大小
     163            0 :     const u64 globalCountsInfoSize = singleRankCountsInfoSize * userRankSize_;
     164              : 
     165            0 :     const u64 bufferSize = inBuffer_.size();
     166            0 :     u32 blockNum = userRankSize_;
     167              : 
     168            0 :     if (enablePingPong) {
     169              :         // 乒乓模式两倍分块
     170            0 :         blockNum = userRankSize_ * PINGPONG_MEM_NUM;
     171            0 :         HCCL_INFO("[AlltoallvContinuousPipeline][SplitBuffer] Use ping-pong mode.");
     172              :     }
     173              : 
     174            0 :     CHK_PRT_RET(blockNum == 0,
     175              :         HCCL_ERROR("[AlltoallvContinuousPipeline][SplitBuffer]Unexpected blockNum[%u].", blockNum), HCCL_E_INTERNAL);
     176              : 
     177              :     // 初始化用于记录buffer中每个分块当作存放了多少数据的vector
     178              :     // 如果是pingpong模式,需要两倍的大小
     179            0 :     inBufferDataSize_.resize(blockNum);
     180              : 
     181            0 :     u64 alignSize = HCCL_MIN_SLICE_ALIGN;
     182            0 :     const u64 minBufferSize = globalCountsInfoSize + alignSize * blockNum;
     183            0 :     CHK_PRT_RET(bufferSize < minBufferSize,
     184              :         HCCL_ERROR("[AlltoallvContinuousPipeline][SplitBuffer]Insufficient buffer size [%llu Byte]; it needs to be "
     185              :                    "greater than [%llu Byte].", bufferSize, minBufferSize), HCCL_E_MEMORY);
     186              : 
     187            0 :     if (bufferSize > globalCountsInfoSize + HCCL_MIN_SLICE_ALIGN_910_93 * blockNum) {
     188            0 :         alignSize = HCCL_MIN_SLICE_ALIGN_910_93;
     189              :     } 
     190              : 
     191            0 :     countsPerBlock_ = (((bufferSize - globalCountsInfoSize) / blockNum) /
     192            0 :         alignSize * alignSize) / unitSize_; // 前面已经可以保证countsPerBlock_大于0,不再检查
     193            0 :     sizePerBlock_ = countsPerBlock_ * unitSize_;
     194              : 
     195            0 :     for (u32 rank = 0; rank < userRankSize_; ++rank) {
     196            0 :         infoOffsets_.emplace_back(sizePerBlock_ * blockNum + singleRankCountsInfoSize * rank);
     197              :     }
     198              : 
     199            0 :     for (u32 blockIdx = 0; blockIdx < blockNum; ++blockIdx) {
     200            0 :         dataBlockOffsets_.emplace_back(sizePerBlock_ * blockIdx);
     201              :     }
     202              : 
     203            0 :     HCCL_INFO("[AlltoallvContinuousPipeline][SplitBuffer] Split buffer done, alignSize[%llu], sizePerBlock[%llu], "
     204              :         "countsPerBlock[%llu], blockNum[%u]", alignSize, sizePerBlock_, countsPerBlock_, blockNum);
     205            0 :     return HCCL_SUCCESS;
     206              : }
     207              : 
     208            0 : HcclResult AlltoallvContinuousPipeline::PartitionSubStreamsAndNotifies(const std::vector<Stream> &subStreams,
     209              :     const std::vector<std::shared_ptr<LocalNotify>> &signalMainToSub,
     210              :     const std::vector<std::shared_ptr<LocalNotify>> &signalSubToMain)
     211              : {
     212            0 :     constexpr u32 DEVICE_EIGHT = 8;
     213            0 :     const u32 sdmaConcurrentNum = std::min(intraRankSize_ - 1, DEVICE_EIGHT);
     214            0 :     const u32 totalSubstreamSize = rdmaConcurrentNum_ + sdmaConcurrentNum;
     215            0 :     CHK_PRT_RET(subStreams.size() < totalSubstreamSize || signalMainToSub.size() < totalSubstreamSize ||
     216              :         signalSubToMain.size() < totalSubstreamSize,
     217              :         HCCL_ERROR("[AlltoallvContinuousPipeline] subStreams size [%u] or signalMainToSub size [%u] or signalSubToMain "
     218              :         "size [%u] is small than totalSubstreamSize [%u].",
     219              :         subStreams.size(),
     220              :         signalMainToSub.size(),
     221              :         signalSubToMain.size(),
     222              :         totalSubstreamSize),
     223              :         HCCL_E_PARA);
     224              : 
     225            0 :     u32 index = 0;
     226              : 
     227              :     // 用于SDMA通信的从流和主从同步notify
     228            0 :     for (u32 i = 0; i < sdmaConcurrentNum; ++i) {
     229            0 :         subStreams_.push_back(subStreams[index]);
     230            0 :         sdmaSubStreams_.push_back(subStreams[index]);
     231            0 :         streamNotifyMainToSdmaSub_.push_back(signalMainToSub[index]);
     232            0 :         streamNotifySdmaSubToMain_.push_back(signalSubToMain[index]);
     233            0 :         index++;
     234              :     }
     235              : 
     236              :     // 用于RDMA通信的从流和主从同步notify
     237            0 :     for (u32 i = 0; i < rdmaConcurrentNum_; ++i) {
     238            0 :         subStreams_.push_back(subStreams[index]);
     239            0 :         rdmaSubStreams_.push_back(subStreams[index]);
     240            0 :         streamNotifyMainToRdmaSub_.push_back(signalMainToSub[index]);
     241            0 :         streamNotifyRdmaSubToMain_.push_back(signalSubToMain[index]);
     242            0 :         index++;
     243              :     }
     244              : 
     245            0 :     HCCL_INFO("[AlltoallvContinuousPipeline][PartitionSubStreamsAndNotifies] Done, sdma: #streams[%zu], "
     246              :               "#notifyMainSub[%zu], #notifySubToMain[%zu]; rdma: #streams[%zu], #notifyMainSub[%zu], "
     247              :               "#notifySubToMain[%zu].",
     248              :         sdmaSubStreams_.size(),
     249              :         streamNotifyMainToSdmaSub_.size(),
     250              :         streamNotifySdmaSubToMain_.size(),
     251              :         rdmaSubStreams_.size(),
     252              :         streamNotifyMainToRdmaSub_.size(),
     253              :         streamNotifyRdmaSubToMain_.size());
     254              : 
     255            0 :     return HCCL_SUCCESS;
     256              : }
     257              : 
     258            0 : inline u32 AlltoallvContinuousPipeline::GetSdmaSubStreamIdx(const u32 remoteRank) const
     259              : {
     260            0 :     return (remoteRank > intraRankId_ ? remoteRank - 1 : remoteRank) % sdmaSubStreams_.size();
     261              : }
     262              : 
     263            0 : inline u64 AlltoallvContinuousPipeline::GetLocalSendCountOfRank(const u32 targetRank) const
     264              : {
     265            0 :     return localSendCounts_[targetRank];
     266              : }
     267              : 
     268            0 : inline u64 AlltoallvContinuousPipeline::GetLocalSendDisplOfRank(const u32 targetRank) const
     269              : {
     270            0 :     return localSendDispls_[targetRank];
     271              : }
     272              : 
     273            0 : inline u64 AlltoallvContinuousPipeline::GetLocalRecvCountOfRank(const u32 sourceRank) const
     274              : {
     275            0 :     return localRecvCounts_[sourceRank];
     276              : }
     277              : 
     278            0 : inline u64 AlltoallvContinuousPipeline::GetLocalRecvDisplOfRank(const u32 sourceRank) const
     279              : {
     280            0 :     return localRecvDispls_[sourceRank];
     281              : }
     282              : 
     283            0 : inline u64 AlltoallvContinuousPipeline::GetDataBlockOffset(const u32 rank, const u32 bufferIdx) const
     284              : {
     285            0 :     if (enablePingPong_) {
     286            0 :         return dataBlockOffsets_[(bufferIdx % PINGPONG_MEM_NUM) * userRankSize_ + rank];
     287              :     }
     288            0 :     return dataBlockOffsets_[rank];
     289              : }
     290              : 
     291            0 : u32 AlltoallvContinuousPipeline::GetLocalLoopNum() const
     292              : {
     293            0 :     u64 maxCount = 0;
     294            0 :     for (u32 rank = 0; rank < userRankSize_; ++rank) {
     295            0 :         if (rank != userRank_) {
     296            0 :             const u64 sendCount = GetLocalSendCountOfRank(rank);
     297            0 :             maxCount = maxCount < sendCount ? sendCount : maxCount;
     298            0 :             const u64 recvCount = GetLocalRecvCountOfRank(rank);
     299            0 :             maxCount = maxCount < recvCount ? recvCount : maxCount;
     300              :         }
     301              :     }
     302              : 
     303              :     // 向上取整
     304            0 :     const u32 localLoopNum = static_cast<u32>((maxCount + countsPerBlock_ - 1) / countsPerBlock_);
     305            0 :     HCCL_INFO("[AlltoallvContinuousPipeline][GetLocalLoopNum] maxCount[%llu], localLoopNum[%u]",
     306              :         maxCount, localLoopNum);
     307            0 :     return localLoopNum;
     308              : }
     309              : 
     310            0 : HcclResult AlltoallvContinuousPipeline::UpdateLocalSendInfo(const u32 targetRank, const u64 count)
     311              : {
     312            0 :     HCCL_DEBUG("[AlltoallvContinuousPipeline][UpdateLocalSendInfo]userRank[%u], count[%llu], before "
     313              :                "update, send info of rank[%u] is [count:%llu, displ:%llu].",
     314              :         userRank_,
     315              :         count,
     316              :         targetRank,
     317              :         localSendCounts_[targetRank],
     318              :         localSendDispls_[targetRank]);
     319              : 
     320            0 :     const u64 maxCount = std::min(localSendCounts_[targetRank], count);
     321            0 :     localSendCounts_[targetRank] -= maxCount;
     322            0 :     localSendDispls_[targetRank] += maxCount;
     323              : 
     324            0 :     HCCL_DEBUG("[AlltoallvContinuousPipeline][UpdateLocalSendInfo]userRank[%u], count[%llu], after "
     325              :                "update, send info of rank[%u] is [count:%llu, displ:%llu].",
     326              :         userRank_,
     327              :         count,
     328              :         targetRank,
     329              :         localSendCounts_[targetRank],
     330              :         localSendDispls_[targetRank]);
     331              : 
     332            0 :     return HCCL_SUCCESS;
     333              : }
     334              : 
     335            0 : HcclResult AlltoallvContinuousPipeline::UpdateLocalRecvInfo(const u32 sourceRank, const u64 count)
     336              : {
     337            0 :     HCCL_DEBUG("[AlltoallvContinuousPipeline][UpdateLocalRecvInfo]userRank[%u], count[%llu], before "
     338              :                "update, receive info of rank[%u] is [count:%llu, displ:%llu].",
     339              :         userRank_,
     340              :         count,
     341              :         sourceRank,
     342              :         localRecvCounts_[sourceRank],
     343              :         localRecvDispls_[sourceRank]);
     344              : 
     345            0 :     const u64 maxCount = std::min(localRecvCounts_[sourceRank], count);
     346            0 :     localRecvCounts_[sourceRank] -= maxCount;
     347            0 :     localRecvDispls_[sourceRank] += maxCount;
     348              : 
     349            0 :     HCCL_DEBUG("[AlltoallvContinuousPipeline][UpdateLocalRecvInfo]userRank[%u], count[%llu], after "
     350              :                "update, receive info of rank[%u] is [count:%llu, displ:%llu].",
     351              :         userRank_,
     352              :         count,
     353              :         sourceRank,
     354              :         localRecvCounts_[sourceRank],
     355              :         localRecvDispls_[sourceRank]);
     356              : 
     357            0 :     return HCCL_SUCCESS;
     358              : }
     359              : 
     360            0 : HcclResult AlltoallvContinuousPipeline::NotifySdmaSubStreamStart()
     361              : {
     362            0 :     for (u32 streamIndex = 0; streamIndex < sdmaSubStreams_.size(); ++streamIndex) {
     363            0 :         CHK_RET(LocalNotify::Post(mainStream_, dispatcher_, streamNotifySdmaSubToMain_[streamIndex],
     364              :             INVALID_VALUE_STAGE));
     365            0 :         CHK_RET(LocalNotify::Wait(
     366              :             sdmaSubStreams_[streamIndex], dispatcher_, streamNotifySdmaSubToMain_[streamIndex], INVALID_VALUE_STAGE));
     367              :     }
     368            0 :     return HCCL_SUCCESS;
     369              : }
     370              : 
     371            0 : HcclResult AlltoallvContinuousPipeline::WaitSdmaSubStreamFinish()
     372              : {
     373            0 :     for (u32 streamIndex = 0; streamIndex < sdmaSubStreams_.size(); ++streamIndex) {
     374            0 :         CHK_RET(LocalNotify::Post(sdmaSubStreams_[streamIndex], dispatcher_, streamNotifyMainToSdmaSub_[streamIndex],
     375              :             INVALID_VALUE_STAGE));
     376            0 :         CHK_RET(LocalNotify::Wait(mainStream_, dispatcher_, streamNotifyMainToSdmaSub_[streamIndex],
     377              :             INVALID_VALUE_STAGE));
     378              :     }
     379            0 :     return HCCL_SUCCESS;
     380              : }
     381              : 
     382            0 : HcclResult AlltoallvContinuousPipeline::NotifyRdmaSubStreamStart()
     383              : {
     384            0 :     for (u32 streamIndex = 0; streamIndex < rdmaSubStreams_.size(); ++streamIndex) {
     385            0 :         CHK_RET(LocalNotify::Post(mainStream_, dispatcher_, streamNotifyRdmaSubToMain_[streamIndex],
     386              :             INVALID_VALUE_STAGE));
     387            0 :         CHK_RET(LocalNotify::Wait(
     388              :             rdmaSubStreams_[streamIndex], dispatcher_, streamNotifyRdmaSubToMain_[streamIndex], INVALID_VALUE_STAGE));
     389              :     }
     390            0 :     return HCCL_SUCCESS;
     391              : }
     392              : 
     393            0 : HcclResult AlltoallvContinuousPipeline::WaitRdmaSubStreamFinish()
     394              : {
     395            0 :     for (u32 streamIndex = 0; streamIndex < rdmaSubStreams_.size(); ++streamIndex) {
     396            0 :         CHK_RET(LocalNotify::Post(rdmaSubStreams_[streamIndex], dispatcher_, streamNotifyMainToRdmaSub_[streamIndex],
     397              :             INVALID_VALUE_STAGE));
     398            0 :         CHK_RET(LocalNotify::Wait(mainStream_, dispatcher_, streamNotifyMainToRdmaSub_[streamIndex],
     399              :             INVALID_VALUE_STAGE));
     400              :     }
     401            0 :     return HCCL_SUCCESS;
     402              : }
     403              : 
     404            0 : HcclResult AlltoallvContinuousPipeline::InterSdmaRx(const LINK& linkLeft, const LINK& linkRight,
     405              :     std::vector<TxMemoryInfo>& sendMems, std::vector<RxMemoryInfo>& recvMems, Stream& stream)
     406              : {
     407            0 :     const bool needRecvFromLinkLeft = !recvMems.empty();
     408            0 :     const bool needSendToLinkRight = !sendMems.empty();
     409              :         
     410              :     // 前同步,通知right我已准备好,可以从我这里读;等待left通知它已准备好,可以从它那里读
     411            0 :     if (needSendToLinkRight) {
     412            0 :         CHK_RET(linkRight->TxAck(stream));
     413              :     }
     414            0 :     if (needRecvFromLinkLeft) {
     415            0 :         CHK_RET(linkLeft->RxAck(stream));
     416              :     }
     417              : 
     418              :     // 从left读
     419            0 :     for (const auto& memInfo : recvMems) {
     420            0 :         void *srcMemPtr = nullptr;
     421            0 :         CHK_RET(linkLeft->GetRemoteMem(memInfo.srcMemType, &srcMemPtr));
     422            0 :         DeviceMem dstMem = DeviceMem::create(memInfo.dst, memInfo.len);
     423            0 :         DeviceMem srcMem(static_cast<s8 *>(srcMemPtr) + memInfo.srcOffset, memInfo.len);
     424            0 :         CHK_RET(HcclD2DMemcpyAsync(dispatcher_, dstMem, srcMem, stream, linkLeft->GetRemoteRank(),
     425              :             linkLeft->GetLinkType()));
     426            0 :     }
     427              : 
     428              :     // 尾同步,通知left我已读完,等待right通知它已读完
     429            0 :     if (needRecvFromLinkLeft) {
     430            0 :         CHK_RET(linkLeft->TxDataSignal(stream));
     431              :     }
     432            0 :     if (needSendToLinkRight) {
     433            0 :         CHK_RET(linkRight->RxDataSignal(stream));
     434              :     }
     435              : 
     436            0 :     HCCL_DEBUG("[AlltoallvContinuousPipeline][InterSdmaRx] Done. linkLeft.rank[%u], linkRight.rank[%u], "
     437              :         "recvMems.size[%zu]", linkLeft->GetRemoteRank(), linkRight->GetRemoteRank(), recvMems.size());
     438            0 :     return HCCL_SUCCESS;
     439              : }
     440              :     
     441              : // 跨module通信,通过RDMA从link left读或向link right写
     442            0 : HcclResult AlltoallvContinuousPipeline::InterRdmaTxRx(const LINK& linkLeft, const LINK& linkRight,
     443              :     std::vector<TxMemoryInfo>& sendMems, std::vector<RxMemoryInfo>& recvMems, Stream& stream)
     444              : {
     445            0 :     const bool needRecvFromLinkLeft = !recvMems.empty();
     446            0 :     const bool needSendToLinkRight = !sendMems.empty();
     447              : 
     448            0 :     if (needRecvFromLinkLeft) {
     449            0 :         CHK_RET(linkLeft->TxAck(stream));
     450              :     }
     451            0 :     if (needSendToLinkRight) {
     452            0 :         CHK_RET(linkRight->RxAck(stream));
     453            0 :         CHK_RET(linkRight->TxAsync(sendMems, stream));
     454              :     }
     455            0 :     if (needRecvFromLinkLeft) {
     456            0 :         CHK_RET(linkLeft->RxAsync(recvMems, stream));
     457            0 :         CHK_RET(linkLeft->PostFinAck(stream));
     458              :     }
     459            0 :     if (needSendToLinkRight) {
     460            0 :         CHK_RET(linkRight->WaitFinAck(stream));
     461              :     }
     462              : 
     463            0 :     HCCL_DEBUG("[AlltoallvContinuousPipeline][InterRdmaTxRx] Done. linkLeft.rank[%u], linkRight.rank[%u], "
     464              :         "sendMems.size[%zu], recvMems.size[%zu]", linkLeft->GetRemoteRank(), linkRight->GetRemoteRank(),
     465              :         sendMems.size(), recvMems.size());
     466            0 :     return HCCL_SUCCESS;
     467              : }
     468              : 
     469            0 : HcclResult AlltoallvContinuousPipeline::LocalCopyFromInputToInBuffer(const u32 targetRank, Stream& stream,
     470              :     const u32 loopIdx)
     471              : {
     472              :     // 根据send displs来计算input的位置,取min(countsPerBlock_, count)个数
     473            0 :     const u64 copyCount = std::min(GetLocalSendCountOfRank(targetRank), countsPerBlock_);
     474            0 :     if (copyCount == 0) {
     475            0 :         return HCCL_SUCCESS;
     476              :     }
     477              : 
     478            0 :     const u64 copySize = copyCount * unitSize_;
     479              : 
     480              :     // 从input拷贝到in buffer对应的分块里
     481            0 :     const u64 srcOffset = GetLocalSendDisplOfRank(targetRank) * unitSize_;
     482            0 :     const u64 dstOffset = GetDataBlockOffset(targetRank, loopIdx);
     483            0 :     DeviceMem src = inputMem_.range(srcOffset, copySize);
     484            0 :     DeviceMem dst = inBuffer_.range(dstOffset, copySize);
     485              : 
     486            0 :     CHK_RET(HcclD2DMemcpyAsync(dispatcher_, dst, src, stream));
     487              : 
     488              :     // 刷新send info
     489            0 :     CHK_RET(UpdateLocalSendInfo(targetRank, copyCount));
     490              : 
     491              :     // 记录in bufer中该分块存放了多少数据
     492            0 :     inBufferDataSize_[targetRank] = copySize;
     493              : 
     494            0 :     HCCL_DEBUG("[AlltoallvContinuousPipeline][LocalCopy][FromInputToInBuffer] done, userRank[%u], targetRank[%u], "
     495              :         "srcOffset[%llu], dstOffset[%llu], copyCount[%llu], copySize[%llu], loopIdx[%u]", userRank_, targetRank,
     496              :         srcOffset, dstOffset, copyCount, copySize, loopIdx);
     497            0 :     return HCCL_SUCCESS;
     498            0 : }
     499              : 
     500            0 : HcclResult AlltoallvContinuousPipeline::LocalCopyFromOutBufferToOutput(const u32 sourceRank, Stream& stream,
     501              :     const u32 loopIdx)
     502              : {
     503            0 :     const u64 copyCount = std::min(GetLocalRecvCountOfRank(sourceRank), countsPerBlock_);
     504            0 :     if (copyCount == 0) {
     505            0 :         return HCCL_SUCCESS;
     506              :     }
     507              :     
     508              :     // 从out buffer对应分块拷贝到output
     509            0 :     const u64 copySize = copyCount * unitSize_;
     510            0 :     const u64 srcOffset = GetDataBlockOffset(sourceRank, loopIdx);
     511            0 :     const u64 dstOffset = GetLocalRecvDisplOfRank(sourceRank) * unitSize_;
     512            0 :     DeviceMem src = outBuffer_.range(srcOffset, copySize);
     513            0 :     DeviceMem dst = outputMem_.range(dstOffset, copySize);
     514              : 
     515            0 :     CHK_RET(HcclD2DMemcpyAsync(dispatcher_, dst, src, stream));
     516              : 
     517              :     // 刷新receive info
     518            0 :     CHK_RET(UpdateLocalRecvInfo(sourceRank, copyCount));
     519              : 
     520            0 :     HCCL_DEBUG("[AlltoallvContinuousPipeline][LocalCopy][FromOutBufferToOutput] done, userRank[%u], sourceRank[%u], "
     521              :         "srcOffset[%llu], dstOffset[%llu], copyCount[%llu], copySize[%llu], loopIdx[%u]", userRank_, sourceRank,
     522              :         srcOffset, dstOffset, copyCount, copySize, loopIdx);
     523            0 :     return HCCL_SUCCESS;
     524            0 : }
     525              : 
     526            0 : HcclResult AlltoallvContinuousPipeline::LocalCopySelfDataFromInputToOutput(Stream& stream)
     527              : {
     528            0 :     const u64 copyCount = GetLocalSendCountOfRank(userRank_);
     529            0 :     if (copyCount == 0) {
     530            0 :         return HCCL_SUCCESS;
     531              :     }
     532              :     
     533              :     // 从input拷贝到output
     534            0 :     const u64 copySize = copyCount * unitSize_;
     535            0 :     const u64 srcOffset = GetLocalSendDisplOfRank(userRank_) * unitSize_;
     536            0 :     const u64 dstOffset = GetLocalRecvDisplOfRank(userRank_) * unitSize_;
     537            0 :     DeviceMem src = inputMem_.range(srcOffset, copySize);
     538            0 :     DeviceMem dst = outputMem_.range(dstOffset, copySize);
     539              : 
     540            0 :     CHK_RET(HcclD2DMemcpyAsync(dispatcher_, dst, src, stream));
     541              : 
     542            0 :     HCCL_DEBUG("[AlltoallvContinuousPipeline][LocalCopy][SelfDataFromInputToOutput] done, userRank[%u], "
     543              :         "srcOffset[%llu], dstOffset[%llu], copyCount[%llu], copySize[%llu]",
     544              :         userRank_, srcOffset, dstOffset, copyCount, copySize);
     545            0 :     return HCCL_SUCCESS;
     546            0 : }
     547              : 
     548            0 : HcclResult AlltoallvContinuousPipeline::SdmaSendFromInputToRemoteOutBuffer(const u32 targetRank, Stream& stream,
     549              :     const u32 loopIdx)
     550              : {
     551            0 :     const u64 sendCount = std::min(GetLocalSendCountOfRank(targetRank), countsPerBlock_);
     552            0 :     if (sendCount == 0) {
     553            0 :         return HCCL_SUCCESS;
     554              :     }
     555              : 
     556              :     // 从input发送到remote out buffer,目的位置是第[本userRank_]个分块
     557            0 :     const u64 sendSize = sendCount * unitSize_;
     558            0 :     const u64 srcOffset = GetLocalSendDisplOfRank(targetRank) * unitSize_;
     559            0 :     const u64 dstOffset = GetDataBlockOffset(userRank_, loopIdx);
     560            0 :     DeviceMem src = inputMem_.range(srcOffset, sendSize);
     561              :     
     562            0 :     const LINK& link = intraLinks_[targetRank % intraRankSize_];
     563            0 :     void *remMemPtr = nullptr;
     564            0 :     CHK_RET(link->GetRemoteMem(UserMemType::OUTPUT_MEM, &remMemPtr));
     565            0 :     DeviceMem dst = DeviceMem::create(static_cast<u8 *>(remMemPtr) + dstOffset, sendSize);
     566              : 
     567              :     // 前后同步在外层处理
     568            0 :     CHK_RET(HcclD2DMemcpyAsync(dispatcher_, dst, src, stream, targetRank, link->GetLinkType()));
     569              : 
     570              :     // 刷新send info
     571            0 :     CHK_RET(UpdateLocalSendInfo(targetRank, sendCount));
     572              : 
     573            0 :     HCCL_DEBUG("[AlltoallvContinuousPipeline][Sdma][SendFromInputToRemoteOutBuffer] done, userRank[%u], "
     574              :         "targetRank[%u], srcOffset[%llu], dstOffset[%llu], sendCount[%llu], sendSize[%llu], loopIdx[%u]",
     575              :         userRank_, targetRank, srcOffset, dstOffset, sendCount, sendSize, loopIdx);
     576            0 :     return HCCL_SUCCESS;
     577            0 : }
     578              : 
     579            0 : HcclResult AlltoallvContinuousPipeline::SdmaReadFromRemoteOutBufferToOutput(const u32 sourceRank, Stream& stream,
     580              :     const u32 loopIdx)
     581              : {
     582              :     // 需要recv counts信息
     583            0 :     CHK_PRT_RET(needCollectInfo_,
     584              :         HCCL_ERROR("[AlltoallvContinuousPipeline][SdmaReadFromRemoteOutBufferToOutput] No receive info."),
     585              :         HCCL_E_INTERNAL);
     586              : 
     587            0 :     const u64 readCount = std::min(GetLocalRecvCountOfRank(sourceRank), countsPerBlock_);
     588            0 :     if (readCount == 0) {
     589            0 :         return HCCL_SUCCESS;
     590              :     }
     591              : 
     592              :     // 从remote out buffer读取到output,源位置是第[sourceRank / intraRankSize_ * intraRankSize_ + intraRankId_]个分块
     593            0 :     const u64 readSize = readCount * unitSize_;
     594            0 :     const u64 srcBlockIdx = sourceRank / intraRankSize_ * intraRankSize_ + intraRankId_;
     595            0 :     const u64 srcOffset = GetDataBlockOffset(srcBlockIdx, loopIdx);
     596            0 :     const u64 dstOffset = GetLocalRecvDisplOfRank(sourceRank) * unitSize_;
     597              :     
     598            0 :     const LINK& link = intraLinks_[sourceRank % intraRankSize_];
     599            0 :     void *remMemPtr = nullptr;
     600            0 :     CHK_RET(link->GetRemoteMem(UserMemType::OUTPUT_MEM, &remMemPtr));
     601            0 :     DeviceMem src = DeviceMem::create(static_cast<u8 *>(remMemPtr) + srcOffset, readSize);
     602              : 
     603            0 :     DeviceMem dst = outputMem_.range(dstOffset, readSize);
     604              : 
     605              :     // 前后同步在外层处理
     606            0 :     CHK_RET(HcclD2DMemcpyAsync(dispatcher_, dst, src, stream, sourceRank, link->GetLinkType()));
     607              : 
     608              :     // 刷新receive info
     609            0 :     CHK_RET(UpdateLocalRecvInfo(sourceRank, readCount));
     610              : 
     611            0 :     HCCL_DEBUG("[AlltoallvContinuousPipeline][Sdma][ReadFromRemoteOutBufferToOutput] done, userRank[%u], "
     612              :         "sourceRank[%u], srcOffset[%llu], dstOffset[%llu], readCount[%llu], readSize[%llu], loopIdx[%u]",
     613              :         userRank_, sourceRank, srcOffset, dstOffset, readCount, readSize, loopIdx);
     614            0 :     return HCCL_SUCCESS;
     615            0 : }
     616              : 
     617            0 : HcclResult AlltoallvContinuousPipeline::InterSendAndReceive(const u32 sendRank, const u32 recvRank, Stream& stream,
     618              :     const u32 loopIdx)
     619              : {
     620              :     // 计算出对端rank所在module的首rank,因为要向对端rank发送[首rank, 首rank+intraRankSize]rank的数据
     621            0 :     const u32 sendModuleFirstRank = sendRank / intraRankSize_ * intraRankSize_;
     622            0 :     const u32 recvModuleFirstRank = recvRank / intraRankSize_ * intraRankSize_;
     623              : 
     624            0 :     std::vector<TxMemoryInfo> sendMems;
     625            0 :     std::vector<RxMemoryInfo> recvMems;
     626            0 :     sendMems.reserve(intraRankSize_);
     627            0 :     recvMems.reserve(intraRankSize_);
     628              : 
     629            0 :     const LINK& sendLink = interLinks_[sendRank / intraRankSize_];
     630            0 :     const LINK& recvLink = interLinks_[recvRank / intraRankSize_];
     631            0 :     const bool isSDMALink = sendLink->IsSpInlineReduce() || recvLink->IsSpInlineReduce();
     632              : 
     633            0 :     for (u32 rankOffset = 0; rankOffset < intraRankSize_; ++rankOffset) {
     634            0 :         const u32 targetRank = sendModuleFirstRank + rankOffset;
     635            0 :         const u64 sendSrcOffset = GetDataBlockOffset(targetRank, loopIdx);
     636            0 :         const u64 sendDstOffset = GetDataBlockOffset(interRankId_ * intraRankSize_ + rankOffset, loopIdx);
     637            0 :         const u64 sendSize = inBufferDataSize_[targetRank];
     638            0 :         inBufferDataSize_[targetRank] = 0;
     639            0 :         if (sendSize > 0) {
     640            0 :             sendMems.emplace_back(TxMemoryInfo{UserMemType::OUTPUT_MEM, sendDstOffset,
     641            0 :                 static_cast<s8*>(inBuffer_.ptr()) + sendSrcOffset, sendSize});
     642              :         }
     643            0 :         HCCL_DEBUG("[AlltoallvContinuousPipeline][InterSendAndReceive]inter send userRank[%u], sendRank[%u], "
     644              :             "targetRank[%u], srcOffset[%llu], dstOffset[%llu], sendSize[%llu], loopIdx[%u]",
     645              :             userRank_, sendRank, targetRank, sendSrcOffset, sendDstOffset, sendSize, loopIdx);
     646              : 
     647            0 :         const u32 sourceRank = recvModuleFirstRank + intraRankId_;
     648            0 :         const u32 actualTargetRank = interRankId_ * intraRankSize_ + rankOffset;
     649            0 :         const u64 recvSrcOffset = GetDataBlockOffset(actualTargetRank, loopIdx);
     650            0 :         const u64 recvDstOffset = GetDataBlockOffset(recvModuleFirstRank + rankOffset, loopIdx);
     651            0 :         const u64 recvCount = std::min(countsPerBlock_, intraRecvCounts_[rankOffset][sourceRank]);
     652            0 :         const u64 recvSize = recvCount * unitSize_;
     653            0 :         if (recvCount > 0) {
     654            0 :             recvMems.emplace_back(RxMemoryInfo{UserMemType::INPUT_MEM, recvSrcOffset,
     655            0 :                 static_cast<s8*>(outBuffer_.ptr()) + recvDstOffset, recvSize});
     656            0 :             intraRecvCounts_[rankOffset][sourceRank] -= recvCount;
     657              :         }
     658            0 :         HCCL_DEBUG("[AlltoallvContinuousPipeline][InterSendAndReceive]inter recv userRank[%u], recvRank[%u], "
     659              :             "sourceRank[%u], targetRank[%u], srcOffset[%llu], dstOffset[%llu], readSize[%llu], loopIdx[%u]",
     660              :             userRank_, recvRank, sourceRank, actualTargetRank, recvSrcOffset, recvDstOffset, recvSize, loopIdx);
     661              :     }
     662            0 :     if (isSDMALink) {
     663              :         // SDMA读
     664            0 :         CHK_RET(InterSdmaRx(recvLink, sendLink, sendMems, recvMems, stream));
     665              :     } else {
     666              :         // RDMA
     667            0 :         CHK_RET(InterRdmaTxRx(recvLink, sendLink, sendMems, recvMems, stream));
     668              :     }
     669              :     
     670            0 :     return HCCL_SUCCESS;
     671            0 : }
     672              : 
     673            0 : HcclResult AlltoallvContinuousPipeline::DoSdmaSync(const SdmaSyncType syncType)
     674              : {
     675            0 :     for (u32 rank = 0; rank < intraRankSize_; ++rank) {
     676            0 :         if (rank == intraRankId_) {
     677            0 :             continue;
     678              :         }
     679            0 :         const u32 streamIndex = GetSdmaSubStreamIdx(rank);
     680            0 :         const LINK& link = intraLinks_[rank];
     681            0 :         Stream& subStream = sdmaSubStreams_[streamIndex];
     682            0 :         if (syncType == SdmaSyncType::PRE_SYNC) {
     683              :             // 前同步
     684            0 :             CHK_RET(link->TxAck(subStream));
     685            0 :             CHK_RET(link->RxAck(subStream));
     686              :         } else {
     687              :             // 尾同步
     688            0 :             CHK_RET(link->TxDataSignal(subStream));
     689            0 :             CHK_RET(link->RxDataSignal(subStream));
     690              :         }
     691              :     }
     692            0 :     HCCL_DEBUG("[AlltoallvContinuousPipeline][DoSdmaSync] Sync done, syncType[%d].", syncType);
     693            0 :     return HCCL_SUCCESS;
     694              : }
     695              : 
     696            0 : HcclResult AlltoallvContinuousPipeline::DoLocalCopy(const u32 beginStepNum, const u32 endStepNum, const u32 loopIdx)
     697              : {
     698            0 :     CHK_PRT_RET(beginStepNum == endStepNum,
     699              :         HCCL_DEBUG("[AlltoallvContinuousPipeline][DoLocalCopy]beginStepNum[%u] == endStepNum[%u], return success.",
     700              :             beginStepNum, endStepNum),
     701              :         HCCL_SUCCESS);
     702              : 
     703            0 :     for (u32 step = beginStepNum + 1; step < endStepNum + 1; ++step) {
     704            0 :         const u32 sendModuleId = (interRankId_ + step) % interRankSize_;
     705            0 :         for (const auto remoteRank : ranksPerModule_[sendModuleId]) {
     706            0 :             CHK_RET(LocalCopyFromInputToInBuffer(remoteRank, mainStream_, loopIdx));
     707              :         }
     708              :     }
     709            0 :     HCCL_DEBUG("[AlltoallvContinuousPipeline][DoLocalCopy] done. beginStepNum[%u], endStepNum[%u], loopIdx[%u]",
     710              :         beginStepNum, endStepNum, loopIdx);
     711            0 :     return HCCL_SUCCESS;
     712              : }
     713              : 
     714            0 : HcclResult AlltoallvContinuousPipeline::DoIntraDistribution(const u32 beginStepNum, const u32 endStepNum,
     715              :     const u32 loopIdx)
     716              : {
     717            0 :     CHK_PRT_RET(beginStepNum == endStepNum,
     718              :         HCCL_DEBUG("[AlltoallvContinuousPipeline][DoIntraDistribution]beginStepNum[%u] == endStepNum[%u], return "
     719              :             "success.", beginStepNum, endStepNum),
     720              :         HCCL_SUCCESS);
     721              : 
     722            0 :     for (u32 step = beginStepNum + 1; step < endStepNum + 1; ++step) {
     723            0 :         const u32 recvModuleId = (interRankId_ + interRankSize_ - step) % interRankSize_;
     724            0 :         HCCL_DEBUG("[AlltoallvContinuousPipeline][DoIntraDistribution] recvModuleId[%u]", recvModuleId);
     725            0 :         for (const auto remoteRank : ranksPerModule_[recvModuleId]) {
     726            0 :             const u32 remoteIntraRank = remoteRank % intraRankSize_;
     727            0 :             HCCL_DEBUG("[AlltoallvContinuousPipeline][DoIntraDistribution] remoteIntraRank[%u]", remoteIntraRank);
     728            0 :             if (intraRankId_ == remoteIntraRank) {
     729              :                 // 如果是同号卡,直接从out buffer拷贝到output
     730            0 :                 CHK_RET(LocalCopyFromOutBufferToOutput(remoteRank, mainStream_, loopIdx));
     731              :             } else {
     732              :                 // 如果不是同号卡,从module内它对应的同号卡获取
     733            0 :                 const u32 streamIndex = GetSdmaSubStreamIdx(remoteIntraRank);
     734            0 :                 HCCL_DEBUG(
     735              :                     "[AlltoallvContinuousPipeline][DoIntraDistribution] streamIndex[%u], sdmaSubStreams_.size()[%zu]",
     736              :                     streamIndex, sdmaSubStreams_.size());
     737            0 :                 Stream& subStream = sdmaSubStreams_[streamIndex];
     738            0 :                 CHK_RET(SdmaReadFromRemoteOutBufferToOutput(remoteRank, subStream, loopIdx));
     739              :             }
     740              :         }
     741              :     }
     742              : 
     743            0 :     HCCL_DEBUG("[AlltoallvContinuousPipeline][DoIntraDistribution] done. beginStepNum[%u], endStepNum[%u], loopIdx[%u]",
     744              :         beginStepNum, endStepNum, loopIdx);
     745            0 :     return HCCL_SUCCESS;
     746              : }
     747              : 
     748            0 : HcclResult AlltoallvContinuousPipeline::DoInterSendReceive(const u32 beginStepNum, const u32 endStepNum,
     749              :     const u32 loopIdx)
     750              : {
     751            0 :     CHK_PRT_RET(beginStepNum == endStepNum,
     752              :         HCCL_DEBUG("[AlltoallvContinuousPipeline][DoInterSendReceive]beginStepNum[%u] == endStepNum[%u], return "
     753              :             "success.", beginStepNum, endStepNum),
     754              :         HCCL_SUCCESS);
     755              : 
     756            0 :     u32 streamIdx = 0;
     757            0 :     for (u32 step = beginStepNum + 1; step < endStepNum + 1; ++step) {
     758            0 :         const u32 sendRank = (userRank_ + step * intraRankSize_) % userRankSize_;
     759            0 :         const u32 recvRank = (userRank_ + userRankSize_ - step * intraRankSize_) % userRankSize_;
     760            0 :         CHK_RET(InterSendAndReceive(sendRank, recvRank, rdmaSubStreams_[streamIdx++], loopIdx));
     761              :     }
     762            0 :     HCCL_DEBUG("[AlltoallvContinuousPipeline][DoInterSendReceive] done. beginStepNum[%u], endStepNum[%u], loopIdx[%u]",
     763              :         beginStepNum, endStepNum, loopIdx);
     764            0 :     return HCCL_SUCCESS;
     765              : }
     766              : 
     767            0 : HcclResult AlltoallvContinuousPipeline::DoLevel0LocalCopy(const u32 loopIdx)
     768              : {
     769            0 :     for (const auto remoteRank : ranksPerModule_[interRankId_]) {
     770            0 :         if (remoteRank == userRank_) {
     771            0 :             continue;
     772              :         }
     773            0 :         CHK_RET(LocalCopyFromOutBufferToOutput(remoteRank, mainStream_, loopIdx));
     774              :     }
     775            0 :     HCCL_DEBUG("[AlltoallvContinuousPipeline][DoLevel0LocalCopy] done, loopIdx[%u].", loopIdx);
     776            0 :     return HCCL_SUCCESS;
     777              : }
     778              : 
     779            0 : HcclResult AlltoallvContinuousPipeline::DoLevel0SdmaSend(const u32 loopIdx)
     780              : {
     781            0 :     for (const auto remoteRank : ranksPerModule_[interRankId_]) {
     782            0 :         if (remoteRank == userRank_) {
     783            0 :             continue;
     784              :         }
     785            0 :         const u32 remoteIntraRank = remoteRank % intraRankSize_;
     786            0 :         const u32 streamIndex = GetSdmaSubStreamIdx(remoteIntraRank);
     787            0 :         Stream& subStream = sdmaSubStreams_[streamIndex];
     788            0 :         CHK_RET(SdmaSendFromInputToRemoteOutBuffer(remoteRank, subStream, loopIdx));
     789              :     }
     790              : 
     791            0 :     HCCL_DEBUG("[AlltoallvContinuousPipeline][DoLevel0SdmaSend] done, loopIdx[%u].", loopIdx);
     792            0 :     return HCCL_SUCCESS;
     793              : }
     794              : 
     795            0 : HcclResult AlltoallvContinuousPipeline::DoIntraInfoBroadcast()
     796              : {
     797            0 :     for (const auto remoteRank : ranksPerModule_[interRankId_]) {
     798            0 :         if (remoteRank == userRank_) {
     799            0 :             continue;
     800              :         }
     801            0 :         const u32 remoteIntraRank = remoteRank % intraRankSize_;
     802            0 :         const u32 streamIndex = GetSdmaSubStreamIdx(remoteIntraRank);
     803            0 :         Stream& subStream = sdmaSubStreams_[streamIndex];
     804              :         
     805            0 :         const LINK& link = intraLinks_[remoteRank % intraRankSize_];
     806            0 :         void *remInPtr = nullptr;
     807            0 :         void *remOutPtr = nullptr;
     808            0 :         CHK_RET(link->GetRemoteMem(UserMemType::INPUT_MEM, &remInPtr));
     809            0 :         CHK_RET(link->GetRemoteMem(UserMemType::OUTPUT_MEM, &remOutPtr));
     810              :         
     811              :         // 前后同步在外层处理,直接发送
     812              :         // 发送counts信息,从output发送到remote out buffer,目的位置是第[本userRank_]个info分块
     813            0 :         const u64 infoSize = userRankSize_ * sizeof(u64);
     814            0 :         const u64 infoOffset = infoOffsets_[userRank_];
     815            0 :         DeviceMem infoSrc = outBuffer_.range(infoOffset, infoSize);
     816            0 :         DeviceMem infoDst = DeviceMem::create(static_cast<u8 *>(remOutPtr) + infoOffset, infoSize);
     817            0 :         CHK_RET(HcclD2DMemcpyAsync(dispatcher_, infoDst, infoSrc, subStream, remoteRank, link->GetLinkType()));
     818              : 
     819              :         // 发送flag,flag值为[LocalLoopNum + 1],从input发送到remote in buffer,目的位置是第[本userRank_]个u32
     820            0 :         const u64 flagSize = sizeof(u32);
     821            0 :         const u64 flagOffset = infoOffsets_[0] + userRank_ * flagSize;
     822            0 :         DeviceMem flagSrc = inBuffer_.range(flagOffset, flagSize);
     823            0 :         DeviceMem flagDst = DeviceMem::create(static_cast<u8 *>(remInPtr) + flagOffset, flagSize);
     824            0 :         CHK_RET(HcclD2DMemcpyAsync(dispatcher_, flagDst, flagSrc, subStream, remoteRank, link->GetLinkType()));
     825              : 
     826            0 :         HCCL_DEBUG("[AlltoallvContinuousPipeline][DoIntraInfoBroadcast] userRank[%u], send info to remoteRank[%u], "
     827              :             "infoOffset[%llu], infoSize[%llu], flagOffset[%llu], flagSize[%llu]",
     828              :             userRank_, remoteRank, infoOffset, infoSize, flagOffset, flagSize);
     829            0 :     }
     830              : 
     831            0 :     HCCL_DEBUG("[AlltoallvContinuousPipeline][DoIntraInfoBroadcast] done.");
     832            0 :     return HCCL_SUCCESS;
     833              : }
     834              : 
     835            0 : HcclResult AlltoallvContinuousPipeline::DoLocalWriteInfoAndFlagAndInterSync()
     836              : {
     837            0 :     HCCL_DEBUG("[AlltoallvContinuousPipeline][DoLocalWriteInfoAndFlagAndInterSync] start.");
     838              : 
     839            0 :     if (!needCollectInfo_) {
     840            0 :         return HCCL_SUCCESS;
     841              :     }
     842              : 
     843              :     // 将counts信息写到out buffer的info区域
     844            0 :     void* infoPtr = localRecvCounts_.data();
     845            0 :     const u64 infoSize = userRankSize_ * sizeof(u64);
     846            0 :     const u64 infoOffset = infoOffsets_[userRank_];
     847            0 :     DeviceMem infoSrc = DeviceMem::create(infoPtr, infoSize);
     848            0 :     DeviceMem infoDst = outBuffer_.range(infoOffset, infoSize);
     849            0 :     CHK_RET(HcclD2DMemcpyAsync(dispatcher_, infoDst, infoSrc, mainStream_));
     850              : 
     851              :     // 将in buffer的flag区域刷0,第[userRank]个u32设为[LocalLoopNum + 1]
     852            0 :     const u64 flagAreaSize = userRankSize_ * sizeof(u32);
     853            0 :     flagAreaRefreshData_[userRank_] = intraLoopNum_ + 1; // +1是应对LocalLoopNum为0的情况
     854            0 :     DeviceMem flagSrc = DeviceMem::create(flagAreaRefreshData_.data(), flagAreaSize);
     855            0 :     DeviceMem flagDst = inBuffer_.range(infoOffsets_[0], flagAreaSize);
     856            0 :     CHK_RET(HcclD2DMemcpyAsync(dispatcher_, flagDst, flagSrc, mainStream_));
     857              : 
     858              :     // 在主流上下一个搬1的任务,kernel可以通过轮询dst是否为1,确保flag区域已被刷值,避免flag区域还是随机值时就开始轮询。
     859            0 :     DeviceMem refreshFlagSrc = DeviceMem::create(&flagAreaRefreshValue_, sizeof(flagAreaRefreshValue_));
     860            0 :     DeviceMem refreshFlagDst = DeviceMem::create(&flagAreaRefreshFlag_, sizeof(flagAreaRefreshFlag_));
     861            0 :     CHK_RET(HcclD2DMemcpyAsync(dispatcher_, refreshFlagDst, refreshFlagSrc, mainStream_));
     862              : 
     863            0 :     CHK_RET(LaunchTask(dispatcher_, mainStream_));
     864              :     
     865            0 :     HCCL_INFO("[AlltoallvContinuousPipeline][DoLocalWriteInfoAndFlagAndInterSync] write counts and flag. userRank[%u], "
     866              :         "infoPtr[%p], infoSize[%llu], infoOffset[%llu]",
     867              :         userRank_, infoPtr, infoSize, infoOffset);
     868            0 :     return HCCL_SUCCESS;
     869            0 : }
     870              : 
     871            0 : HcclResult AlltoallvContinuousPipeline::WaitValueOfRank(const u32 rank, const HcclUs &startTimeUs, u32 &value)
     872              : {
     873            0 :     const auto* valuePtr = reinterpret_cast<u32 *>(static_cast<u8 *>(inBuffer_.ptr()) + infoOffsets_[0]) + rank;
     874            0 :     HcclUs lastUt = startTimeUs;
     875            0 :     constexpr s64 timeout = 1800 * 1000 * 1000; // 超时时间暂定为1800s
     876            0 :     constexpr s64 printStateInterval = 30 * 1000 * 1000; // 每隔30s打印一次状态
     877            0 :     HCCL_DEBUG("[AlltoallvContinuousPipeline][WaitValueOfRank] start waiting value of rank[%u], valuePtr[%p].",
     878              :         rank, valuePtr);
     879              : 
     880            0 :     while (flagAreaRefreshFlag_ == 0 || *valuePtr == 0) {
     881            0 :         const HcclUs currentUt = TIME_NOW();
     882              :         // 等待value过程,每隔30秒打印一次状态
     883            0 :         if (DURATION_US(currentUt - lastUt).count() > printStateInterval) {
     884            0 :             lastUt = currentUt;
     885            0 :             if (flagAreaRefreshFlag_ == 0) {
     886            0 :                 HCCL_RUN_INFO("[AlltoallvContinuousPipeline][WaitValueOfRank] The Previous task has not been completed."
     887              :                     " userRank[%u]", userRank_);
     888              :             } else {
     889            0 :                 HCCL_RUN_INFO("[AlltoallvContinuousPipeline][WaitValueOfRank] waiting value of rank[%u]", rank);
     890              :             }
     891              :         }
     892              : 
     893            0 :         CHK_PRT_RET(DURATION_US(currentUt - startTimeUs).count() > timeout,
     894              :             HCCL_ERROR("[AlltoallvContinuousPipeline][WaitValueOfRank] Waiting for the value of rank[%u] timed out.",
     895              :                 rank),
     896              :             HCCL_E_TIMEOUT);
     897              :     }
     898            0 :     value = *valuePtr;
     899              : 
     900              :     // 每次执行算子开头都有重置flag区域的task,所以此处不需要重置为0
     901              : 
     902            0 :     HCCL_DEBUG("[AlltoallvContinuousPipeline][WaitValueOfRank] Got value of rank[%u], value[%u].", rank, value);
     903            0 :     return HCCL_SUCCESS;
     904              : }
     905              : 
     906            0 : HcclResult AlltoallvContinuousPipeline::WaitAndCalReceiveInfo()
     907              : {
     908              :     // 计算自己以及module内其他卡的receive count
     909            0 :     HCCL_DEBUG("[AlltoallvContinuousPipeline][WaitAndCalReceiveInfo] start.");
     910            0 :     const HcclUs startUt = TIME_NOW();
     911              : 
     912            0 :     for (u32 intraRankIdx = 0; intraRankIdx < intraRankSize_; ++intraRankIdx) {
     913            0 :         if (intraRankIdx == intraRankId_) {
     914            0 :             continue;
     915              :         }
     916            0 :         const u32 remoteRank = interRankId_ * intraRankSize_ + intraRankIdx;
     917            0 :         u32 remoteValue = 0;
     918            0 :         CHK_RET(WaitValueOfRank(remoteRank, startUt, remoteValue));
     919              : 
     920              :         const auto *countsPtr =
     921            0 :             reinterpret_cast<u64 *>(static_cast<u8 *>(outBuffer_.ptr()) + infoOffsets_[remoteRank]);
     922            0 :         HCCL_DEBUG("[AlltoallvContinuousPipeline][WaitAndCalReceiveInfo] remoteRank[%u], infoOffset[%llu], "
     923              :             "countsPtr[%p]", remoteRank, infoOffsets_[remoteRank], countsPtr);
     924              : 
     925            0 :         for (u32 i = 0; i < userRankSize_; ++i) {
     926            0 :             HCCL_DEBUG("[AlltoallvContinuousPipeline][WaitAndCalReceiveInfo] countsPtr[%u]=[%llu]", i, countsPtr[i]);
     927            0 :             intraRecvCounts_[intraRankIdx][i] = countsPtr[i];
     928              :         }
     929              : 
     930            0 :         const u32 remoteLoopNum = remoteValue - 1;  // remoteValue一定大于0
     931            0 :         intraLoopNum_ = std::max(intraLoopNum_, remoteLoopNum);
     932            0 :         HCCL_DEBUG("[AlltoallvContinuousPipeline][WaitAndCalReceiveInfo] remoteRank[%u], remoteLoopNum[%u], "
     933              :             "intraLoopNum_[%u]", remoteRank, remoteLoopNum, intraLoopNum_);
     934              :     }
     935              :     
     936            0 :     HCCL_DEBUG("[AlltoallvContinuousPipeline][WaitAndCalReceiveInfo] done. loopNum[%u]", intraLoopNum_);
     937            0 :     return HCCL_SUCCESS;
     938              : }
     939              : 
     940            0 : HcclResult AlltoallvContinuousPipeline::RunAsync()
     941              : {
     942              :     // 在开始前,先将counts信息拷贝到info区域,并且刷新一下flag区域
     943            0 :     CHK_RET(DoLocalWriteInfoAndFlagAndInterSync());
     944              : 
     945              :     // 按照机间pairwise的方式计算每轮的步数,等于level1的rank size - 1
     946            0 :     const u32 stepsPerLoop = interRankSize_ - 1;
     947              : 
     948              :     // 需要发给其他module的每块数据都会经历三步:本地拷贝至in buffer、经RDMA链路发送到同号卡、由同号卡用SDMA分发到接收卡
     949            0 :     TaskState localCopyState;
     950            0 :     TaskState interState;
     951            0 :     TaskState intraState;
     952              : 
     953            0 :     localCopyState.stepNumNext = std::min(rdmaConcurrentNum_, stepsPerLoop);
     954              : 
     955              :     // 外层loop,要重复多少轮,默认为0,在获取到全局counts信息后刷新
     956            0 :     u32 repeatLoopNum = 0;
     957              : 
     958              :     // 第一步,需要把counts信息广播给机内其他rank
     959            0 :     bool needDoIntraInfoBroadcast = needCollectInfo_;
     960              : 
     961            0 :     while (localCopyState.stepNum < stepsPerLoop || interState.stepNum < stepsPerLoop ||
     962            0 :            intraState.stepNum < stepsPerLoop) {
     963              :         // 每一轮首次做跨module收发的同时,做level0的SDMA写,每张卡从input写到对端的out buffer
     964            0 :         const bool needDoLevel0SdmaWrite = (interState.stepNumNext != 0 && interState.stepNum == 0);
     965              :         // 每一轮首次做机内分发的同时,每张卡从out buffer将level0其他卡发来的数据拷至output
     966            0 :         const bool needDoLevel0LocalCopy = (intraState.stepNumNext != 0 && intraState.stepNum == 0);
     967              : 
     968              :         // intra的stepNum小于stepNumNext,说明本轮需要做intra分发(SDMA)
     969            0 :         const bool needDoIntraTasks = intraState.stepNum < intraState.stepNumNext;
     970              :         // inter的stepNum小于stepNumNext,说明本轮需要做inter收发(RDMA)
     971            0 :         const bool needDoInterTasks = interState.stepNum < interState.stepNumNext;
     972              : 
     973            0 :         const bool hasSdmaTask = needDoIntraTasks || needDoLevel0SdmaWrite || needDoIntraInfoBroadcast;
     974            0 :         const bool hasRdmaTask = needDoInterTasks;
     975              : 
     976            0 :         HCCL_DEBUG("[AlltoallvContinuousPipeline][RunAsyncLoop][start] localCopy[step:%u, next:%u, loop:%u], "
     977              :             "inter[step:%u, next:%u, loop:%u], intra[step:%u, next:%u, loop:%u]",
     978              :             localCopyState.stepNum, localCopyState.stepNumNext, localCopyState.loopNum,
     979              :             interState.stepNum, interState.stepNumNext, interState.loopNum,
     980              :             intraState.stepNum, intraState.stepNumNext, intraState.loopNum);
     981              : 
     982            0 :         HCCL_DEBUG("[AlltoallvContinuousPipeline][RunAsyncLoop] needDoLevel0SdmaWrite[%d], needDoLevel0LocalCopy[%d], "
     983              :             "hasSdmaTask[%d], hasRdmaTask[%d]", needDoLevel0SdmaWrite, needDoLevel0LocalCopy, hasSdmaTask, hasRdmaTask);
     984              : 
     985            0 :         if (hasSdmaTask) {
     986              :             // 本轮有SDMA任务。主流通知SDMA从流,SDMA从流等待主流,前同步
     987            0 :             CHK_RET(NotifySdmaSubStreamStart());
     988            0 :             CHK_RET(DoSdmaSync(SdmaSyncType::PRE_SYNC));
     989              : 
     990              :             // 下发一组主从流同步,拉齐SDMA任务,避免任务不同时拉起导致性能下降
     991            0 :             CHK_RET(WaitSdmaSubStreamFinish());
     992            0 :             CHK_RET(NotifySdmaSubStreamStart());
     993              :         }
     994              : 
     995            0 :         if (hasRdmaTask) {
     996              :             // 本轮有RDMA任务。主流通知RDMA从流,RDMA从流等待主流
     997            0 :             CHK_RET(NotifyRdmaSubStreamStart());
     998              :         }
     999              : 
    1000            0 :         if (needDoLevel0SdmaWrite) {
    1001            0 :             CHK_RET(DoLevel0SdmaSend(interState.loopNum));
    1002              : 
    1003            0 :             if (interState.loopNum == 0) {
    1004              :                 // 首轮,本卡input到output的拷贝也在这时做
    1005            0 :                 CHK_RET(LocalCopySelfDataFromInputToOutput(mainStream_));
    1006              :             }
    1007              :         }
    1008              : 
    1009            0 :         if (interState.loopNum == 0 && needDoInterTasks) {
    1010              :             // 第一轮,在做inter分发前,等待、获取receive信息
    1011            0 :             if (needCollectInfo_) {
    1012            0 :                 CHK_RET(WaitAndCalReceiveInfo()); // 阻塞函数
    1013            0 :                 needCollectInfo_ = false;
    1014              :             }
    1015              : 
    1016              :             // 刷新重复轮数:总轮数-1
    1017            0 :             repeatLoopNum = intraLoopNum_ > 0 ? intraLoopNum_ - 1 : 0;
    1018            0 :             if (localCopyState.stepNum == stepsPerLoop && localCopyState.loopNum < repeatLoopNum) {
    1019              :                 // 如果需要做多轮,在此处立即刷新local copy stepNum,让第二轮的任务尽早开始
    1020            0 :                 ++localCopyState.loopNum;
    1021            0 :                 localCopyState.stepNum = 0;
    1022            0 :                 localCopyState.stepNumNext = std::min(rdmaConcurrentNum_, stepsPerLoop);
    1023              :             }
    1024              :         }   
    1025              : 
    1026              :         // intraStepNum小于interStepNum,表示需要做机内分发
    1027            0 :         CHK_RET(DoIntraDistribution(intraState.stepNum, intraState.stepNumNext, intraState.loopNum));
    1028              :         // interStepNum小于localCopyStepNum,表示需要做机间收发
    1029            0 :         CHK_RET(DoInterSendReceive(interState.stepNum, interState.stepNumNext, interState.loopNum));
    1030              :         // localCopyStepNum小于stepsPerLoop,根据并发度拷贝需要的数据到in buffer
    1031            0 :         CHK_RET(DoLocalCopy(localCopyState.stepNum, localCopyState.stepNumNext, localCopyState.loopNum));
    1032              : 
    1033            0 :         if (needDoIntraInfoBroadcast) {
    1034              :             // 第一轮第一步,机内广播本卡的counts信息
    1035            0 :             CHK_RET(DoIntraInfoBroadcast());
    1036            0 :             needDoIntraInfoBroadcast = false;
    1037              :         }
    1038              : 
    1039            0 :         if (needDoLevel0LocalCopy) {
    1040            0 :             CHK_RET(DoLevel0LocalCopy(intraState.loopNum));
    1041              :         }
    1042              : 
    1043            0 :         if (hasSdmaTask) {
    1044              :             // SDMA尾同步,主流等待SDMA从流,SDMA从流通知主流
    1045            0 :             CHK_RET(DoSdmaSync(SdmaSyncType::POST_SYNC));
    1046            0 :             CHK_RET(WaitSdmaSubStreamFinish());
    1047              :         }
    1048            0 :         if (hasRdmaTask) {
    1049              :             // 主流等待RDMA从流,RDMA从流通知主流
    1050            0 :             CHK_RET(WaitRdmaSubStreamFinish());
    1051              :         }
    1052              : 
    1053              :         // 下发task
    1054            0 :         CHK_RET(LaunchTaskExtend(dispatcher_, mainStream_, subStreams_));
    1055              : 
    1056              :         // 更新每种任务的当前步数
    1057            0 :         intraState.stepNum = intraState.stepNumNext;
    1058            0 :         interState.stepNum = interState.stepNumNext;
    1059            0 :         localCopyState.stepNum = localCopyState.stepNumNext;
    1060              : 
    1061              :         // 更新每种任务的下一步目标步数
    1062            0 :         intraState.stepNumNext = interState.stepNumNext;
    1063            0 :         interState.stepNumNext = localCopyState.stepNumNext;
    1064            0 :         localCopyState.stepNumNext = std::min(localCopyState.stepNumNext + rdmaConcurrentNum_, stepsPerLoop);
    1065              : 
    1066              :         // 检查是否需要重复执行,若需要,将对应的step num刷回为0
    1067            0 :         if (intraState.stepNum == stepsPerLoop && intraState.loopNum < interState.loopNum) {
    1068            0 :             ++intraState.loopNum;
    1069            0 :             intraState.stepNum = 0;
    1070            0 :             intraState.stepNumNext = std::min(rdmaConcurrentNum_, stepsPerLoop);
    1071              :         }
    1072            0 :         if (interState.stepNum == stepsPerLoop && interState.loopNum < localCopyState.loopNum) {
    1073            0 :             ++interState.loopNum;
    1074            0 :             interState.stepNum = 0;
    1075            0 :             interState.stepNumNext = std::min(rdmaConcurrentNum_, stepsPerLoop);
    1076              :         }
    1077            0 :         if (localCopyState.stepNum == stepsPerLoop && localCopyState.loopNum < repeatLoopNum) {
    1078            0 :             ++localCopyState.loopNum;
    1079            0 :             localCopyState.stepNum = 0;
    1080            0 :             localCopyState.stepNumNext = std::min(rdmaConcurrentNum_, stepsPerLoop);
    1081              :         }
    1082              : 
    1083            0 :         HCCL_DEBUG("[AlltoallvContinuousPipeline][RunAsyncLoop][end] localCopy[step:%u, next:%u, loop:%u], "
    1084              :             "inter[step:%u, next:%u, loop:%u], intra[step:%u, next:%u, loop:%u]",
    1085              :             localCopyState.stepNum, localCopyState.stepNumNext, localCopyState.loopNum,
    1086              :             interState.stepNum, interState.stepNumNext, interState.loopNum,
    1087              :             intraState.stepNum, intraState.stepNumNext, intraState.loopNum);
    1088              :     }
    1089            0 :     return HCCL_SUCCESS;
    1090              : }
    1091              : 
    1092              : REGISTER_TEMPLATE(TemplateType::TEMPLATE_ALL_2_ALL_V_CONTINUOUS_PIPELINE, AlltoallvContinuousPipeline);
    1093              : } // namespace hccl
        

Generated by: LCOV version 2.0-1