LCOV - code coverage report
Current view: top level - legacy/ascend910/algorithm/base/alg_template - alg_template_multi_deter_pipeline.cc (source / functions) Coverage Total Hit
Test: coverage.info Lines: 0.0 % 288 0
Test Date: 2026-08-18 17:47:01 Functions: 0.0 % 33 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 "alg_template_multi_deter_pipeline.h"
      12              : namespace hccl {
      13            0 : MultiDeterPipeline::MultiDeterPipeline(const HcclDispatcher dispatcher) : AlgTemplateBase(dispatcher) {}
      14              : 
      15            0 : MultiDeterPipeline::~MultiDeterPipeline() {}
      16              : 
      17            0 : HcclResult MultiDeterPipeline::RunAsync() { return HCCL_SUCCESS; }
      18              : 
      19              : // ReduceScatterDeterPipeline
      20            0 : HcclResult MultiDeterPipeline::Prepare(
      21              :     [[maybe_unused]] HcomCollOpInfo* opInfo, [[maybe_unused]] DeviceMem& buffer, [[maybe_unused]] const u64 count,
      22              :     [[maybe_unused]] const u64 offset, [[maybe_unused]] const std::vector<Slice>& slices,
      23              :     [[maybe_unused]] const SubCommInfo& level0CommInfo, [[maybe_unused]] const SubCommInfo& level1CommInfo,
      24              :     [[maybe_unused]] Stream& mainStream, [[maybe_unused]] std::vector<Stream>& subStream,
      25              :     [[maybe_unused]] std::vector<std::shared_ptr<LocalNotify>>& notifyMain,
      26              :     [[maybe_unused]] std::vector<std::shared_ptr<LocalNotify>>& notifySub)
      27              : {
      28            0 :     return HCCL_SUCCESS;
      29              : }
      30              : 
      31              : // AllReduceDeterPipeline
      32            0 : HcclResult MultiDeterPipeline::Prepare(
      33              :     [[maybe_unused]] HcomCollOpInfo* opInfo, [[maybe_unused]] DeviceMem& inBuffer,
      34              :     [[maybe_unused]] DeviceMem& outBuffer, [[maybe_unused]] const u64 count,
      35              :     [[maybe_unused]] const std::vector<Slice>& slices, [[maybe_unused]] const SubCommInfo& level0CommInfo,
      36              :     [[maybe_unused]] const SubCommInfo& level1CommInfo, [[maybe_unused]] Stream& mainStream,
      37              :     [[maybe_unused]] std::vector<Stream>& subStream,
      38              :     [[maybe_unused]] std::vector<std::shared_ptr<LocalNotify>>& notifyMain,
      39              :     [[maybe_unused]] std::vector<std::shared_ptr<LocalNotify>>& notifySub)
      40              : {
      41            0 :     return HCCL_SUCCESS;
      42              : }
      43              : 
      44            0 : HcclResult MultiDeterPipeline::MainWaitSub(u32 begin, u32 end)
      45              : {
      46            0 :     for (u32 signalIndex = begin; signalIndex < end; signalIndex++) {
      47            0 :         CHK_RET(LocalNotify::Wait(mainStream_, dispatcher_, streamNotifyMain_[signalIndex], INVALID_VALUE_STAGE));
      48              :     }
      49            0 :     return HCCL_SUCCESS;
      50              : }
      51              : 
      52            0 : HcclResult MultiDeterPipeline::SubRecordMain(u32 begin, u32 end)
      53              : {
      54            0 :     for (u32 streamIndex = begin; streamIndex < end; streamIndex++) {
      55            0 :         CHK_RET(LocalNotify::Post(subStreams_[streamIndex], dispatcher_, streamNotifyMain_[streamIndex], -1));
      56              :     }
      57            0 :     return HCCL_SUCCESS;
      58              : }
      59              : 
      60            0 : HcclResult MultiDeterPipeline::MainRecordSub(u32 begin, u32 end)
      61              : {
      62            0 :     for (u32 signalIndex = begin; signalIndex < end; signalIndex++) {
      63            0 :         CHK_RET(LocalNotify::Post(mainStream_, dispatcher_, streamNotifySub_[signalIndex], -1));
      64              :     }
      65            0 :     return HCCL_SUCCESS;
      66              : }
      67              : 
      68              : // begin max = 7, end max = 11
      69            0 : HcclResult MultiDeterPipeline::SubWaitMain(u32 begin, u32 end)
      70              : {
      71            0 :     for (u32 streamIndex = begin; streamIndex < end; streamIndex++) {
      72            0 :         CHK_RET(LocalNotify::Wait(
      73              :             subStreams_[streamIndex], dispatcher_, streamNotifySub_[streamIndex], INVALID_VALUE_STAGE));
      74              :     }
      75            0 :     return HCCL_SUCCESS;
      76              : }
      77              : 
      78            0 : HcclResult MultiDeterPipeline::GetRemoteCclbufferDeviceMem(
      79              :     [[maybe_unused]] u32 inputSliceIndex, [[maybe_unused]] LINK link, [[maybe_unused]] u32 outputSliceIndex,
      80              :     [[maybe_unused]] DeviceMem& remoteMem)
      81              : {
      82            0 :     return HCCL_SUCCESS;
      83              : }
      84              : 
      85              : HcclResult
      86            0 : MultiDeterPipeline::GetLocalUserInDeviceMem([[maybe_unused]] u32 rankIdInAllRanks, [[maybe_unused]] DeviceMem& locaMem)
      87              : {
      88            0 :     return HCCL_SUCCESS;
      89              : }
      90              : 
      91            0 : HcclResult MultiDeterPipeline::GetLocalUserOutDeviceMem(
      92              :     [[maybe_unused]] u32 rankIdInAllRanks, [[maybe_unused]] DeviceMem& localMem)
      93              : {
      94            0 :     return HCCL_SUCCESS;
      95              : }
      96              : 
      97            0 : HcclResult MultiDeterPipeline::GetLocalInCclbufferDeviceMem(
      98              :     [[maybe_unused]] u32 rankIdInAllRanks, [[maybe_unused]] DeviceMem& localMem, [[maybe_unused]] bool ifUseLastSize)
      99              : {
     100            0 :     return HCCL_SUCCESS;
     101              : }
     102              : 
     103            0 : HcclResult MultiDeterPipeline::GetLocalOutCclbufferDeviceMem(
     104              :     [[maybe_unused]] u32 rankIdInAllRanks, [[maybe_unused]] DeviceMem& localMem, [[maybe_unused]] bool ifUseLastSize)
     105              : {
     106            0 :     return HCCL_SUCCESS;
     107              : }
     108              : 
     109            0 : HcclResult MultiDeterPipeline::RunLocalCopy() { return HCCL_SUCCESS; }
     110              : 
     111            0 : HcclResult MultiDeterPipeline::RunIntraAlltoallPreSync([[maybe_unused]] u32 step) { return HCCL_SUCCESS; }
     112              : 
     113            0 : HcclResult MultiDeterPipeline::RunIntraAlltoall(u32 step)
     114              : {
     115            0 :     u32 recvServerId = GetPreServerIdByStep(step);  // 从上一个收 2
     116            0 :     u32 sendServerId = GetNextServerIdByStep(step); // 发给发下一个 1
     117              :     // 机内alltoall full mesh收集数据,是为了收集机内第sendServerId整块的内存(包含intraRankId_块)
     118              :     // 该索引是为了计算第sendServerId整块内的内存序号块(0~intraRankId_-1)
     119            0 :     std::vector<u32> localUsrInIndex;
     120            0 :     for (u32 i = intraRankId_ + 1; i < intraRankSize_ + intraRankId_; ++i) {
     121            0 :         localUsrInIndex.push_back(i % intraRankSize_);
     122              :     }
     123            0 :     for (u32 i = 0; i < intraRankSize_ - 1; ++i) {
     124            0 :         u32 sendIntraRankId = GetNextIntraRankIdByStep(i + 1);
     125            0 :         LINK sendIntraLink = intraLinks_[sendIntraRankId];
     126            0 :         DeviceMem srcMem;
     127            0 :         DeviceMem dstMem;
     128              :         // 从usrin收集发给下一个cclbufer的数据, 收集的所有数据需要发送给机间序号为sendServerId的server
     129            0 :         u32 needSendInputndex = GetRankIdx(sendServerId, localUsrInIndex[i]);
     130              :         // 发送数据到cclbufer 索引为[intraRankId_, localUsrInIndex[i]]
     131            0 :         u32 recvIntraRankIdx = alltoallRecvBlockIdxMap_[intraRankId_][localUsrInIndex[i]];
     132            0 :         u32 recvCclbufferIndex = GetRankIdx(recvServerId, recvIntraRankIdx);
     133            0 :         CHK_RET(GetLocalUserInDeviceMem(needSendInputndex, srcMem));
     134            0 :         CHK_RET(GetRemoteCclbufferDeviceMem(needSendInputndex, sendIntraLink, recvCclbufferIndex, dstMem));
     135              :         // 发送给机内rank索引 [serverId_, sendIntraRankId]
     136            0 :         u32 remoteUserRank = GetRankIdx(serverId_, sendIntraRankId);
     137              :         // SDMA copy write语义,因为只能写到cclbuffer
     138            0 :         CHK_RET(HcclD2DMemcpyAsync(
     139              :             dispatcher_, dstMem, srcMem, subStreams_[i], remoteUserRank, sendIntraLink->GetLinkType()));
     140            0 :         CHK_RET(sendIntraLink->TxDataSignal(subStreams_[i]));
     141            0 :         CHK_RET(sendIntraLink->RxDataSignal(subStreams_[i]));
     142            0 :         HCCL_DEBUG("[%s] intra-server SDMA send, intraRank: [%u] -> [%u]", __func__, intraRankId_, sendIntraRankId);
     143            0 :         HCCL_DEBUG(
     144              :             "[%s] intra-server SDMA send, mem: inputMem[%u, %u] -> cclbuffer[%u, %u]; cclbufferNo[%u] -> [%u]",
     145              :             __func__, sendServerId, localUsrInIndex[i], recvServerId, recvIntraRankIdx, needSendInputndex,
     146              :             recvCclbufferIndex);
     147            0 :     }
     148            0 :     HCCL_INFO("[%s] intra-server step[%u] run alltoall success", __func__, step);
     149            0 :     return HCCL_SUCCESS;
     150            0 : }
     151              : 
     152            0 : HcclResult MultiDeterPipeline::GroupTasksByStream(
     153              :     u32 activeCount, const std::vector<bool>& isReduceBlock, u32 retIndex,
     154              :     std::vector<std::vector<std::vector<std::pair<u32, u32>>>>& batchStreamTasks, // 输出:批次→流→任务
     155              :     std::vector<bool>& processed, std::vector<u32>& origIdxMap, u32& newActiveCount)
     156              : {
     157            0 :     batchStreamTasks.clear(); // 清空批次任务
     158            0 :     processed.assign(activeCount, false);
     159            0 :     newActiveCount = 0;
     160              : 
     161            0 :     const u32 mergeStep = 2;
     162            0 :     const u32 batchSize = MAX_REDUCE_STREAM_NUM;
     163            0 :     const u32 totalGroups = (activeCount + mergeStep - 1) / mergeStep;
     164            0 :     const u32 batchNum = (totalGroups + batchSize - 1) / batchSize;
     165              : 
     166              :     // 逐批生成流任务
     167            0 :     for (u32 batch = 0; batch < batchNum; batch++) {
     168              :         // 初始化当前批次的流任务(MAX_REDUCE_STREAM_NUM条流)
     169            0 :         std::vector<std::vector<std::pair<u32, u32>>> streamTasks(MAX_REDUCE_STREAM_NUM);
     170            0 :         u32 startGroup = batch * batchSize;
     171            0 :         u32 endGroup = std::min((batch + 1) * batchSize, totalGroups);
     172              : 
     173              :         // 处理当前批次的分组
     174            0 :         for (u32 group = startGroup; group < endGroup; group++) {
     175            0 :             u32 idx0 = group * mergeStep;
     176            0 :             u32 idx1 = idx0 + 1;
     177            0 :             if (idx1 >= activeCount) {
     178            0 :                 processed[idx0] = false;
     179            0 :                 continue;
     180              :             }
     181              : 
     182              :             // 选择dst/src(原有优先级逻辑不变)
     183              :             u32 dstIdx, srcIdx;
     184            0 :             if (origIdxMap[idx0] == retIndex) { // 当前idx0的原始索引是目标块,强制为dst
     185            0 :                 dstIdx = idx0;
     186            0 :                 srcIdx = idx1;
     187            0 :             } else if (origIdxMap[idx1] == retIndex) { // 当前idx1的原始索引是目标块,强制为dst
     188            0 :                 dstIdx = idx1;
     189            0 :                 srcIdx = idx0;
     190            0 :             } else if (isReduceBlock[idx0] && !isReduceBlock[idx1]) {
     191            0 :                 dstIdx = idx0;
     192            0 :                 srcIdx = idx1;
     193            0 :             } else if (!isReduceBlock[idx0] && isReduceBlock[idx1]) {
     194            0 :                 dstIdx = idx1;
     195            0 :                 srcIdx = idx0;
     196              :             } else {
     197            0 :                 dstIdx = std::max(idx0, idx1);
     198            0 :                 srcIdx = std::min(idx0, idx1);
     199              :             }
     200              : 
     201              :             // 分配到当前批次的流任务中
     202            0 :             u32 batchInnerGroupIdx = group - startGroup;
     203            0 :             u32 streamId = batchInnerGroupIdx % MAX_REDUCE_STREAM_NUM;
     204            0 :             streamTasks[streamId].emplace_back(srcIdx, dstIdx);
     205              : 
     206            0 :             processed[srcIdx] = true;
     207            0 :             processed[dstIdx] = false;
     208              : 
     209            0 :             HCCL_DEBUG(
     210              :                 "[%s] batch[%u] group[%u] merge src[%u] -> dst[%u] on stream[%u]", __func__, batch, group, srcIdx,
     211              :                 dstIdx, streamId + reduceStreamBegin_);
     212              :         }
     213              : 
     214              :         // 将当前批次的流任务加入总批次列表
     215            0 :         batchStreamTasks.push_back(streamTasks);
     216            0 :     }
     217              : 
     218            0 :     newActiveCount = std::count(processed.begin(), processed.end(), false);
     219            0 :     return HCCL_SUCCESS;
     220              : }
     221              : 
     222            0 : HcclResult MultiDeterPipeline::BatchPostNotifyForStreams(
     223              :     [[maybe_unused]] const std::vector<std::vector<std::pair<u32, u32>>>& streamTasks,
     224              :     [[maybe_unused]] bool isStartPhase, [[maybe_unused]] bool useMainStream)
     225              : {
     226            0 :     return HCCL_SUCCESS;
     227              : }
     228              : 
     229            0 : HcclResult MultiDeterPipeline::ExecuteStreamTasks(
     230              :     const std::vector<std::vector<std::pair<u32, u32>>>& streamTasks, const std::vector<DeviceMem>& validMem,
     231              :     std::vector<u32>& origIdxMap, bool useMainStream)
     232              : {
     233            0 :     for (u32 s = 0; s < MAX_REDUCE_STREAM_NUM; s++) {
     234            0 :         if (streamTasks[s].empty())
     235            0 :             continue;
     236              : 
     237            0 :         u32 streamIdx = reduceStreamBegin_ + s;
     238            0 :         Stream& subStream = subStreams_[streamIdx];
     239            0 :         Stream& stream = useMainStream ? mainStream_ : subStream;
     240              : 
     241            0 :         for (const auto& task : streamTasks[s]) {
     242            0 :             u32 srcIdx = task.first;
     243            0 :             u32 dstIdx = task.second;
     244            0 :             const DeviceMem& dstMem = validMem[dstIdx];
     245            0 :             const DeviceMem& srcMem = validMem[srcIdx];
     246            0 :             u64 count = srcMem.size() / unitSize_;
     247            0 :             CHK_RET(HcclReduceAsync(
     248              :                 dispatcher_, srcMem.ptr(), count, dataType_, reductionOp_, stream, dstMem.ptr(), INVALID_VALUE_RANKID,
     249              :                 LinkType::LINK_ONCHIP, INLINE_REDUCE_BIT));
     250            0 :             HCCL_DEBUG(
     251              :                 "[%s] stream[%u] execute task: merge src[%u] -> dst[%u], origSrc[%u] -> origDst[%u]", __func__,
     252              :                 useMainStream ? 0 : streamIdx, srcIdx, dstIdx, origIdxMap[srcIdx], origIdxMap[dstIdx]);
     253              :         }
     254              :     }
     255            0 :     return HCCL_SUCCESS;
     256              : }
     257              : 
     258            0 : void MultiDeterPipeline::CompressActiveSet(
     259              :     std::vector<DeviceMem>& validMem, std::vector<bool>& isReduceBlock, std::vector<u32>& origIdxMap,
     260              :     const std::vector<bool>& processed, u32& trackedTargetIdx, const u32 origRetIndex)
     261              : {
     262            0 :     std::vector<DeviceMem> newValidMem;
     263            0 :     std::vector<bool> newIsReduceBlock;
     264            0 :     std::vector<u32> newOrigIdxMap;
     265            0 :     u32 newTrackedTargetIdx = 0;
     266            0 :     bool foundTarget = false;
     267              : 
     268              :     // 保留未处理的块(processed=false,即dst块)
     269            0 :     for (u32 i = 0; i < validMem.size(); i++) {
     270            0 :         if (!processed[i]) { // 仅保留dst块,移除src块(processed=true)
     271            0 :             newValidMem.push_back(validMem[i]);
     272            0 :             newIsReduceBlock.push_back(isReduceBlock[i]);
     273            0 :             newOrigIdxMap.push_back(origIdxMap[i]);
     274              : 
     275              :             // 追踪原始目标块(retIndex)的新索引
     276            0 :             if (!foundTarget && origIdxMap[i] == origRetIndex) {
     277            0 :                 newTrackedTargetIdx = newValidMem.size() - 1;
     278            0 :                 foundTarget = true;
     279              :             }
     280              :         }
     281              :     }
     282              : 
     283            0 :     validMem.swap(newValidMem);
     284            0 :     isReduceBlock.swap(newIsReduceBlock);
     285            0 :     origIdxMap.swap(newOrigIdxMap);
     286              :     // 未找到目标块时,默认指向最后一个块
     287            0 :     trackedTargetIdx = foundTarget ? newTrackedTargetIdx : (validMem.empty() ? 0 : validMem.size() - 1);
     288            0 :     HCCL_DEBUG(
     289              :         "[%s] compressed: old size[%llu], new size[%llu], trackedTargetIdx[%u]", __func__, processed.size(),
     290              :         validMem.size(), trackedTargetIdx);
     291            0 : }
     292              : 
     293            0 : HcclResult MultiDeterPipeline::LocalReduce(
     294              :     std::vector<DeviceMem>& reduceMem, std::vector<bool>& isReduceBlock, u32 retIndex, bool useMainStream)
     295              : {
     296            0 :     const u32 totalBlockCount = reduceMem.size();
     297              :     // 校验1:容器大小匹配 + retIndex越界
     298            0 :     if (reduceMem.size() != isReduceBlock.size() || retIndex >= totalBlockCount) {
     299            0 :         HCCL_ERROR(
     300              :             "[%s] Invalid param (size mismatch: %llu vs %llu, retIndex: %u >= %u)", __func__, reduceMem.size(),
     301              :             isReduceBlock.size(), retIndex, totalBlockCount);
     302            0 :         return HCCL_E_PARA;
     303              :     }
     304              :     // 校验2:目标内存块有效
     305            0 :     const DeviceMem& targetCCLBuffer = reduceMem[retIndex];
     306            0 :     if (targetCCLBuffer.ptr() == nullptr || targetCCLBuffer.size() == 0) {
     307            0 :         HCCL_ERROR(
     308              :             "[%s] Target CCLBuffer invalid (ptr: %p, size: %llu)", __func__, targetCCLBuffer.ptr(),
     309              :             targetCCLBuffer.size());
     310            0 :         return HCCL_E_MEMORY;
     311              :     }
     312              : 
     313            0 :     std::vector<DeviceMem> validMem = std::move(reduceMem); // 外层不使用reduceMem
     314            0 :     std::vector<bool> validIsReduceBlock = std::move(isReduceBlock);
     315              :     // 1. 动态追踪目标块索引 2. 原索引→当前索引的映射表
     316            0 :     std::vector<u32> origIdxMap(validMem.size());
     317            0 :     for (size_t i = 0; i < origIdxMap.size(); ++i) {
     318            0 :         origIdxMap[i] = i;
     319              :     }
     320              : 
     321            0 :     if (validMem.size() == 1) {
     322            0 :         HCCL_ERROR("[%s] validMem size is one, only target block valid", __func__);
     323            0 :         return HCCL_E_PARA;
     324              :     }
     325              : 
     326            0 :     u32 trackedTargetIdx = retIndex;
     327            0 :     u32 activeCount = validMem.size();
     328            0 :     const u32 origRetIndex = retIndex;
     329              : 
     330            0 :     while (activeCount > 1) {
     331            0 :         std::vector<std::vector<std::vector<std::pair<u32, u32>>>> batchStreamTasks; // 批次→流→任务
     332            0 :         std::vector<bool> processed(activeCount, false);
     333            0 :         u32 newActiveCount = 0;
     334              : 
     335              :         // 2.1 生成按批次组织的流任务
     336            0 :         CHK_RET(GroupTasksByStream(
     337              :             activeCount, validIsReduceBlock, origRetIndex, batchStreamTasks, processed, origIdxMap, newActiveCount));
     338              : 
     339              :         // 2.2 逐批执行流任务(串行处理每批)
     340            0 :         for (const auto& streamTasks : batchStreamTasks) {
     341              :             // a. 执行start phase notify(仅处理有任务的流)
     342            0 :             CHK_RET(BatchPostNotifyForStreams(streamTasks, true, useMainStream));
     343              :             // b. 执行当前批次的流任务(src→dst归约)
     344            0 :             CHK_RET(ExecuteStreamTasks(streamTasks, validMem, origIdxMap, useMainStream));
     345              :             // c. 执行sync phase notify(等待当前批次完成)
     346            0 :             CHK_RET(BatchPostNotifyForStreams(streamTasks, false, useMainStream));
     347              :         }
     348              :         // 2.3 压缩活跃块(移除src块,保留dst块)
     349            0 :         CompressActiveSet(validMem, validIsReduceBlock, origIdxMap, processed, trackedTargetIdx, origRetIndex);
     350            0 :         activeCount = validMem.size();
     351            0 :         HCCL_DEBUG("[LocalReduce] round done: activeCount=%u -> %u", newActiveCount, activeCount);
     352            0 :     }
     353            0 :     HCCL_DEBUG(
     354              :         "[%s] Local reduce success (merge to retIndex[%u] CCLBuffer, final tracked idx[%u])", __func__, retIndex,
     355              :         trackedTargetIdx);
     356            0 :     return HCCL_SUCCESS;
     357            0 : }
     358              : 
     359            0 : HcclResult MultiDeterPipeline::RunIntraLocalReduce([[maybe_unused]] u32 step) { return HCCL_SUCCESS; }
     360              : 
     361            0 : HcclResult MultiDeterPipeline::RunInterSend([[maybe_unused]] u32 step) { return HCCL_SUCCESS; }
     362              : 
     363            0 : HcclResult MultiDeterPipeline::RunFinalReduce() { return HCCL_SUCCESS; }
     364              : 
     365            0 : HcclResult MultiDeterPipeline::AlltoallSync([[maybe_unused]] u32 step, [[maybe_unused]] bool isStartPhase)
     366              : {
     367            0 :     return HCCL_SUCCESS;
     368              : }
     369              : 
     370            0 : HcclResult MultiDeterPipeline::LocalReduceSync([[maybe_unused]] u32 step, [[maybe_unused]] bool isStartPhase)
     371              : {
     372            0 :     return HCCL_SUCCESS;
     373              : }
     374              : 
     375            0 : HcclResult MultiDeterPipeline::AlltoallLocalReduceSync(u32 step, bool isStartPhase)
     376              : {
     377            0 :     bool alltoallStep = (step < allSteps_);
     378            0 :     bool localReduceStep = (step > 1 && step < allSteps_ + 1);
     379            0 :     if (alltoallStep) {
     380            0 :         CHK_RET(AlltoallSync(step, isStartPhase));
     381              :     }
     382            0 :     if (localReduceStep) {
     383            0 :         CHK_RET(LocalReduceSync(step, isStartPhase));
     384              :     }
     385            0 :     return HCCL_SUCCESS;
     386              : }
     387              : 
     388            0 : HcclResult MultiDeterPipeline::RunAsyncLocalReduceSerial()
     389              : {
     390            0 :     HCCL_INFO(
     391              :         "[MultiDeterPipeline] run begin: rank[%u] ranksize[%u] inputMem[%p] outputMem[%p]", userRank_, userRankSize_,
     392              :         usrInMemPtr_, usrOutMemPtr_);
     393            0 :     CHK_SMART_PTR_NULL(dispatcher_);
     394              :     // 以机内8卡为例主流 + 从流 = 1 + 7 + 4 = 12
     395            0 :     allSteps_ = serverSize_;
     396              :     // #1 机间发送,#2 local reduce,#3 机内alltoall
     397              :     // 以机间的pairwise来分步,#n表示pairwise的第n步
     398            0 :     for (u32 step = 1; step < allSteps_ + 1; step++) {
     399            0 :         HCCL_DEBUG(
     400              :             "[%s] userRank[%u], intraRankId[%u], serverId[%u], step[%u/%u] begin", __func__, userRank_, intraRankId_,
     401              :             serverId_, step, allSteps_);
     402              :         // alltoall 主从流同步+前同步+拉齐
     403            0 :         CHK_RET(AlltoallSync(step, true));
     404            0 :         if (step < allSteps_ - 1) {
     405            0 :             CHK_RET(RunIntraAlltoallPreSync(step));
     406              :         }
     407            0 :         if (step == allSteps_ - 1) {
     408            0 :             CHK_RET(RunIntraAlltoallPreSync(0));
     409              :         }
     410            0 :         if (step == 1) {
     411            0 :             CHK_RET(RunLocalCopy());
     412              :         }
     413            0 :         if (step > 1) {
     414            0 :             CHK_RET(RunInterSend(step - 1));
     415              :         }
     416              :         // #1 机内RS alltoall + local reduce串行
     417            0 :         if (step < allSteps_) {
     418            0 :             CHK_RET(RunIntraAlltoall(step));
     419            0 :             CHK_RET(AlltoallSync(step, false));
     420            0 :             CHK_RET(LocalReduceSync(step, true));
     421            0 :             CHK_RET(RunIntraLocalReduce(step));
     422            0 :             CHK_RET(LocalReduceSync(step, false));
     423              :         } else {
     424              :             // #0 机内RS alltoall + local reduce串行,#0不需要向其他机发送数据
     425            0 :             CHK_RET(RunIntraAlltoall(0));
     426            0 :             CHK_RET(AlltoallSync(0, false));
     427            0 :             CHK_RET(LocalReduceSync(0, true));
     428            0 :             CHK_RET(RunIntraLocalReduce(0));
     429            0 :             CHK_RET(LocalReduceSync(0, false));
     430              :         }
     431              :     }
     432              :     // 总local reduce
     433            0 :     CHK_RET(RunFinalReduce());
     434            0 :     HCCL_INFO("[MultiDeterPipeline] MultiDeterPipeline success userRank[%u] ", userRank_);
     435            0 :     return HCCL_SUCCESS;
     436              : }
     437              : 
     438              : // 每个server内首先要进行alltoall full mesh收集数据,再进行机内local reduce,最后发送给指定server
     439            0 : HcclResult MultiDeterPipeline::RunAsyncReduceScatterPipeline()
     440              : {
     441            0 :     constexpr u64 HCCL_MEDIUM_COUNT_2_MB = 2 * 1024 * 1024;
     442              :     // 2机或者数据量小于2MB走localreduce串行算法
     443            0 :     if (serverSize_ <= LOCAL_REDUCE_SERIIAL_ALG_SERVER_NUM || GetLocalReduceSerialThresh() < HCCL_MEDIUM_COUNT_2_MB) {
     444            0 :         CHK_RET(RunAsyncLocalReduceSerial());
     445            0 :         return HCCL_SUCCESS;
     446              :     }
     447            0 :     HCCL_INFO("[MultiDeterPipeline] [%s] begin, userRank[%u]", __func__, userRank_);
     448              :     // 以机内8卡为例主流 + 从流 = 1 + 7 + 4 = 12
     449              :     // pairwise总共需要serverSize_步,#1机内alltoall只能自己执行,无法和其他步骤并行,所以总共需要serverSize_ + 1个步骤
     450            0 :     allSteps_ = serverSize_ + 1;
     451              :     // #1 机间发送,#2 local reduce,#3 机内alltoall
     452              :     // 以机间的pairwise来分步,#n表示pairwise的第n步
     453            0 :     for (u32 step = 1; step < allSteps_ + 1; step++) {
     454            0 :         HCCL_DEBUG(
     455              :             "[%s] userRank[%u], intraRankId[%u], serverId[%u], step[%u/%u] begin", __func__, userRank_, intraRankId_,
     456              :             serverId_, step, allSteps_);
     457            0 :         CHK_RET(AlltoallLocalReduceSync(step, true));
     458            0 :         if (step == 1) {
     459            0 :             CHK_RET(RunLocalCopy());
     460              :         }
     461            0 :         if (step < allSteps_ - 1) {
     462            0 :             CHK_RET(RunIntraAlltoallPreSync(step));
     463              :         }
     464            0 :         if (step == allSteps_ - 1) {
     465            0 :             CHK_RET(RunIntraAlltoallPreSync(0));
     466              :         }
     467            0 :         if (step > STEP_OFFSET_TWO) {
     468            0 :             CHK_RET(RunInterSend(step - STEP_OFFSET_TWO));
     469              :         }
     470              :         // allSteps_最小为3
     471            0 :         if (step > 1 && step < allSteps_) {
     472            0 :             CHK_RET(RunIntraLocalReduce(step - 1));
     473              :         }
     474              :         // 最后一步,需要额外执行 #0 机内RS local reduce,#0不需要向其他机发送数据
     475            0 :         if (step == allSteps_) {
     476            0 :             CHK_RET(RunIntraLocalReduce(0));
     477              :         }
     478            0 :         if (step < allSteps_ - 1) {
     479            0 :             CHK_RET(RunIntraAlltoall(step));
     480              :         }
     481            0 :         if (step == allSteps_ - 1) {
     482              :             // #0 机内RS alltoall
     483            0 :             CHK_RET(RunIntraAlltoall(0));
     484              :         }
     485            0 :         CHK_RET(AlltoallLocalReduceSync(step, false));
     486              :     }
     487              :     // 总local reduce
     488            0 :     CHK_RET(RunFinalReduce());
     489            0 :     HCCL_INFO("[MultiDeterPipeline] [%s] end, userRank[%u]", __func__, userRank_);
     490            0 :     return HCCL_SUCCESS;
     491              : }
     492              : 
     493              : // 遍历所有发送方rank和发送方块索引,预计算映射关系,目的是构造出下属矩阵
     494              : // srcRank\srcBlockIdx  0       1       2
     495              : //         0           MAX      0       0
     496              : //         1            0  MAX  1
     497              : //         2            1       1  MAX
     498              : // 例1,发送方是rank0,接收端是rank1,那么接收端非自身 rank 列表为[0, 2],那么rank0的索引为0,所以就发到rank1的第0块内存
     499              : // 例2,发送方是rank0,接收端是rank2,那么接收端非自身 rank 列表为[0, 1],那么rank0的索引为0,所以就发到rank2的第0块内存
     500              : // 例3,发送方是rank1,接收端是rank2,那么接收端非自身 rank 列表为[0, 1],那么rank1的索引为1,所以就发到rank2的第1块内存
     501            0 : void MultiDeterPipeline::InitAlltoallRecvBlockIdxMap()
     502              : {
     503            0 :     const u32 rankSize = intraRankSize_;
     504            0 :     alltoallRecvBlockIdxMap_.resize(rankSize, std::vector<u32>(rankSize, UINT32_MAX));
     505            0 :     if (rankSize <= 1) {
     506            0 :         return;
     507              :     }
     508              :     // 规则一:接收端的块索引 = 发送方 rank 在「接收端非自身 rank 列表」中的索引
     509            0 :     std::vector<std::vector<u32>> dstRankToRankIndex(rankSize, std::vector<u32>(rankSize, UINT32_MAX));
     510            0 :     for (u32 dstRank = 0; dstRank < rankSize; ++dstRank) {
     511            0 :         for (u32 srcRank = 0; srcRank < rankSize; ++srcRank) {
     512            0 :             if (srcRank == dstRank) {
     513            0 :                 continue;
     514              :             }
     515              :             // 若 srcRank < dstRank:索引 = srcRank, 若 srcRank > dstRank:索引 = srcRank - 1
     516            0 :             const u32 idx = (srcRank < dstRank) ? srcRank : (srcRank - 1);
     517            0 :             dstRankToRankIndex[dstRank][srcRank] = idx;
     518              :         }
     519              :     }
     520            0 :     for (u32 srcRank = 0; srcRank < rankSize; ++srcRank) {
     521            0 :         for (u32 srcBlockIdx = 0; srcBlockIdx < rankSize; ++srcBlockIdx) {
     522              :             // 规则二:发送方块索引 = 接收端 rank
     523            0 :             const u32 dstRank = srcBlockIdx;
     524              :             // 跳过自身发送的无效场景
     525            0 :             if (srcRank == dstRank) {
     526            0 :                 continue;
     527              :             }
     528              :             // 查找发送方rank在列表中的索引,存入映射表
     529            0 :             const u32 dstBlockIdx = dstRankToRankIndex[dstRank][srcRank];
     530            0 :             alltoallRecvBlockIdxMap_[srcRank][srcBlockIdx] = dstBlockIdx;
     531            0 :             HCCL_DEBUG(
     532              :                 "[%s] srcRank[%u], srcBlockIdx[%u] -> dstRank[%u], dstBlockIdx[%u]", __func__, srcRank, srcBlockIdx,
     533              :                 dstRank, dstBlockIdx);
     534              :         }
     535              :     }
     536            0 : }
     537              : 
     538            0 : HcclResult MultiDeterPipeline::PrepareTopoInfo(const SubCommInfo& level0CommInfo, const SubCommInfo& level1CommInfo)
     539              : {
     540            0 :     serverSize_ = level1CommInfo.localRankSize;
     541            0 :     CHK_PRT_RET(
     542              :         serverSize_ < MIN_SERVER_NUM,
     543              :         HCCL_ERROR("[%s] Unexpected inter rank size[%u], which should >= 2.", __func__, serverSize_), HCCL_E_PARA);
     544              : 
     545            0 :     intraRankSize_ = level0CommInfo.localRankSize;
     546            0 :     CHK_PRT_RET(
     547              :         intraRankSize_ < MIN_INTRA_RANK_NUM,
     548              :         HCCL_ERROR("[%s] Unexpected intra rank size[%u], which should >= 3.", __func__, intraRankSize_), HCCL_E_PARA);
     549            0 :     intraRankId_ = level0CommInfo.localRank;
     550            0 :     serverId_ = level1CommInfo.localRank;
     551            0 :     userRankSize_ = intraRankSize_ * serverSize_;
     552            0 :     userRank_ = intraRankId_ + serverId_ * intraRankSize_;
     553              : 
     554            0 :     intraLinks_ = level0CommInfo.links;  // 节点内
     555            0 :     serverLinks_ = level1CommInfo.links; // 节点间
     556            0 :     HCCL_INFO(
     557              :         "[%s] opInfo: dataType[%u], unitSize[%u], memSliceSize[%u], usrInMem[%p], usrOutMem[%p], reductionOp[%u]",
     558              :         __func__, dataType_, unitSize_, memSliceSize_, usrInMemPtr_, usrOutMemPtr_, reductionOp_);
     559            0 :     HCCL_INFO(
     560              :         "[%s] topoInfo: userRank[%u], intraRankId[%u], intraRankSize[%u], serverId[%u], interRankSize[%u]", __func__,
     561              :         userRank_, intraRankId_, intraRankSize_, serverId_, serverSize_);
     562            0 :     HCCL_INFO(
     563              :         "[%s] topoInfo: severLinksNum[%zu], intraLinksNum[%zu]", __func__, serverLinks_.size(), intraLinks_.size());
     564            0 :     InitAlltoallRecvBlockIdxMap();
     565            0 :     return HCCL_SUCCESS;
     566              : }
     567              : } // namespace hccl
        

Generated by: LCOV version 2.0-1