LCOV - code coverage report
Current view: top level - legacy/ascend910/framework/device/framework - aicpu_cache_manager.cc (source / functions) Coverage Total Hit
Test: coverage.info Lines: 2.9 % 552 16
Test Date: 2026-08-18 17:47:01 Functions: 21.1 % 19 4

            Line data    Source code
       1              : /**
       2              :  * Copyright (c) 2025 Huawei Technologies Co., Ltd.
       3              :  * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
       4              :  * CANN Open Software License Agreement Version 2.0 (the "License").
       5              :  * Please refer to the License for details. You may not use this file except in compliance with the License.
       6              :  * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
       7              :  * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
       8              :  * See LICENSE in the root of the software repository for the full text of the License.
       9              :  */
      10              : 
      11              : #include "aicpu_cache_manager.h"
      12              : 
      13              : #include "comm_utils.h"
      14              : #include "transport_pub.h"
      15              : #include "dispatcher.h"
      16              : #include "dispatcher_aicpu_pub.h"
      17              : #include "log.h"
      18              : #include "task_logic_info_pub.h"
      19              : #include "profiling_manager_device.h"
      20              : #include "alltoall_utils_pub.h"
      21              : 
      22              : namespace hccl {
      23           33 : AicpuCacheManager::AicpuCacheManager() { HCCL_RUN_INFO("Construct AicpuCacheManager complete."); }
      24              : 
      25           32 : AicpuCacheManager::~AicpuCacheManager()
      26              : {
      27              :     // 释放算子展开的动态缓存 (if any)
      28           32 :     if (opUnfoldCachePtr_ != nullptr) {
      29           11 :         delete opUnfoldCachePtr_;
      30           11 :         opUnfoldCachePtr_ = nullptr;
      31              :     }
      32              : 
      33           32 :     HCCL_RUN_INFO("Destruct AicpuCacheManager success!");
      34           32 : }
      35              : 
      36            1 : void AicpuCacheManager::SetSymmetricMemoryEnable(bool enable)
      37              : {
      38            1 :     isSymmetricMemory_ = enable;
      39            1 :     HCCL_INFO("[AicpuCacheManager][SetSymmetricMemoryEnable] enable[%u]", enable);
      40            1 : }
      41              : 
      42           11 : HcclResult AicpuCacheManager::InitOpUnfoldCache()
      43              : {
      44              :     // 创建算子展开的动态缓存 (不区分单算子/图模式)
      45           11 :     HCCL_INFO("[AicpuCacheManager][InitOpUnfoldCache] create aicpu cache for operator unfolding");
      46           11 :     opUnfoldCachePtr_ = (new (std::nothrow) OpUnfoldCache());
      47           11 :     CHK_PTR_NULL(opUnfoldCachePtr_);
      48              : 
      49           11 :     return HCCL_SUCCESS;
      50              : }
      51              : 
      52            0 : HcclResult AicpuCacheManager::LookupOpUnfoldCache(
      53              :     const std::string& algName, const OpParam& param, const AlgResourceResponse& algResource, bool& needExecute,
      54              :     bool& isCacheMiss, Stream& mainStream, std::vector<Stream>& slaveStreams, void* dispatcherPtr,
      55              :     const bool isDeviceMode, const HcclTopoInfo& topoinfo, std::unique_ptr<TopoMatcher>& topoMatcherPtr,
      56              :     const AlgOpContext& algContext, std::shared_ptr<AicpuZeroCopyExchanger>& zeroCopyExchangerPtr,
      57              :     const HcclWorkflowMode workflowMode, const DeviceMem& tinySendRecvMem,
      58              :     std::function<HcclResult()> setProfStartCallback)
      59              : {
      60            0 :     needExecute = true;
      61            0 :     isCacheMiss = false;
      62              : 
      63            0 :     CHK_PTR_NULL(opUnfoldCachePtr_);
      64              : 
      65              :     // Dump main stream and slave streams addr and id for debug
      66            0 :     HCCL_INFO(
      67              :         "[AicpuCacheManager][LookupOpUnfoldCache] mainStream with streamId[%u] and id[%u]",
      68              :         mainStream.GetHcclStreamInfo().actualStreamId, mainStream.id());
      69            0 :     for (size_t i = 0; i < slaveStreams.size(); ++i) {
      70            0 :         Stream& slaveStream = slaveStreams[i];
      71            0 :         HCCL_INFO(
      72              :             "[AicpuCacheManager][LookupOpUnfoldCache] %uth slaveStream with streamId[%u] and id[%u]", i,
      73              :             slaveStream.GetHcclStreamInfo().actualStreamId, slaveStream.id());
      74              :     }
      75              : 
      76              :     // 判断是否需要cache
      77            0 :     bool needCache = false;
      78            0 :     CHK_RET(NeedOpUnfoldCache(
      79              :         algName, param, algResource, isDeviceMode, topoinfo, topoMatcherPtr, algContext, workflowMode, needCache));
      80            0 :     HCCL_INFO("[AicpuCacheManager][LookupOpUnfoldCache] needCache[%u]", needCache);
      81              : 
      82              :     // Cacheable算子
      83            0 :     if (needCache) {
      84              :         // 将streams中已有的task强制下发, 放置cache缓存跟算子编排无关的SQE
      85              :         // 注意: cache miss需要先强制下发, 避免缓存和算子展开无关的SQE; cache hit也需要强制下发,
      86              :         // 否则LaunchNewTask只会下发cache里的, 而不会下发stream里的
      87            0 :         CHK_PTR_NULL(dispatcherPtr);
      88            0 :         CHK_RET(LaunchTaskExtend(dispatcherPtr, mainStream, slaveStreams));
      89              : 
      90              :         // 准备key
      91            0 :         OpUnfoldKey opUnfoldKey;
      92            0 :         CHK_RET(GetOpUnfoldKey(param, opUnfoldKey, topoinfo, algContext, workflowMode));
      93            0 :         HCCL_INFO(
      94              :             "[AicpuCacheManager][LookupOpUnfoldCache] prepare key[%s] for op-unfold cache",
      95              :             opUnfoldKey.GetKeyString().c_str());
      96              : 
      97              :         // 准备 memory ranges
      98            0 :         std::vector<OpUnfoldMemRange> userInputMemRanges;
      99            0 :         std::vector<OpUnfoldMemRange> userOutputMemRanges;
     100            0 :         CHK_RET(PrepareUserMemRanges(
     101              :             param, algResource, userInputMemRanges, userOutputMemRanges, topoinfo, zeroCopyExchangerPtr, workflowMode,
     102              :             tinySendRecvMem));
     103              : 
     104              :         // 查找算子展开的动态缓存
     105            0 :         HCCL_INFO(
     106              :             "[AicpuCacheManager][LookupOpUnfoldCache] look up op-unfold cache for key %s",
     107              :             opUnfoldKey.GetKeyString().c_str());
     108            0 :         OpUnfoldCacheEntry* entryPtr = nullptr;
     109            0 :         CHK_RET(opUnfoldCachePtr_->FindEntry(opUnfoldKey, &entryPtr));
     110            0 :         if (entryPtr != nullptr) { // Cache hit
     111            0 :             HCCL_INFO(
     112              :                 "[AicpuCacheManager][LookupOpUnfoldCache] cache hit for key %s", opUnfoldKey.GetKeyString().c_str());
     113              : 
     114              :             // 判断是否为alltoallv算子
     115            0 :             CHK_PTR_NULL(dispatcherPtr);
     116            0 :             CHK_PTR_NULL(setProfStartCallback);
     117            0 :             const bool profL1Enable = dfx::ProfilingManager::GetProfL1State(); // SQE-level profiling info
     118            0 :             if (IsAlltoallvType(param.opType)) { // alltoallv类算子, 需要额外的offset信息
     119              :                 // 准备offset信息
     120            0 :                 AlltoallvSendRecvInfo alltoallvSendRecvInfo;
     121            0 :                 CHK_RET(PrepareAlltoallvSendRecvInfo(param, alltoallvSendRecvInfo, topoinfo));
     122              : 
     123              :                 // 刷新缓存的SQE并直接下发到RTSQ
     124              :                 // 注意: AicpuCacheManager下dispatcher一定是DispatcherAicpu
     125            0 :                 (void)setProfStartCallback(); // Keep consistent with cache miss (调用kfcHandler for kSetProfTimeStart)
     126            0 :                 CHK_RET((reinterpret_cast<DispatcherAiCpu*>(dispatcherPtr))
     127              :                             ->LaunchNewTask(
     128              :                                 entryPtr, userInputMemRanges, userOutputMemRanges, mainStream, slaveStreams,
     129              :                                 profL1Enable, true, alltoallvMetadata_, alltoallvSendRecvInfo));
     130            0 :             } else { // 非V类算子, 无需offset信息
     131              :                 // 刷新缓存的SQE并直接下发到RTSQ
     132              :                 // 注意: AicpuCacheManager下dispatcher一定是DispatcherAicpu
     133            0 :                 (void)setProfStartCallback(); // Keep consistent with cache miss (调用kfcHandler for kSetProfTimeStart)
     134            0 :                 CHK_RET((reinterpret_cast<DispatcherAiCpu*>(dispatcherPtr))
     135              :                             ->LaunchNewTask(
     136              :                                 entryPtr, userInputMemRanges, userOutputMemRanges, mainStream, slaveStreams,
     137              :                                 profL1Enable, false, AlltoallvMetadata(), AlltoallvSendRecvInfo()));
     138              :             }
     139              : 
     140              :             // 不需要执行算子展开的具体编排
     141            0 :             needExecute = false;
     142              :         } else { // Cache miss
     143            0 :             HCCL_INFO(
     144              :                 "[AicpuCacheManager][LookupOpUnfoldCache] cache miss for key %s", opUnfoldKey.GetKeyString().c_str());
     145              : 
     146              :             // alltoallv类算子需要传入metadata, 用于HCCL input buffer的扫描, 判断SQE addr字段对应的rank
     147              :             // id用于后续地址刷新
     148            0 :             bool isAlltoallv = false;
     149            0 :             const AlltoallvMetadata* alltoallvMetadataPtr = nullptr;
     150            0 :             if (IsAlltoallvType(param.opType)) {
     151            0 :                 isAlltoallv = true;
     152            0 :                 alltoallvMetadataPtr = &alltoallvMetadata_;
     153            0 :                 CHK_PTR_NULL(alltoallvMetadataPtr);
     154              :             }
     155              : 
     156              :             // 设置launch context, enable DispatcherAicpu在下发SQE时去执行cache admission
     157              :             // 注意: AicpuCacheManager下dispatcher一定是DispatcherAicpu
     158            0 :             CHK_RET((reinterpret_cast<DispatcherAiCpu*>(dispatcherPtr))
     159              :                         ->SetLaunchContext(
     160              :                             opUnfoldKey, opUnfoldCachePtr_, userInputMemRanges, userOutputMemRanges, isAlltoallv,
     161              :                             alltoallvMetadataPtr));
     162              : 
     163            0 :             isCacheMiss = true;
     164              :         }
     165            0 :     }
     166              : 
     167            0 :     return HCCL_SUCCESS;
     168              : }
     169              : 
     170            0 : HcclResult AicpuCacheManager::PreProcessForCacheMiss(const OpParam& param, std::unique_ptr<CollExecutorBase>& executor)
     171              : {
     172              :     // 第一个需要cache的alltoallv类算子
     173              :     // 注意: 只有当alltoallv的algName为"RunAlltoAllDirectFullmesh"时, 才会进入cache,
     174              :     // 所以使用的一定是CollRunAlltoAllDirectFullmesh executor
     175            0 :     if (IsAlltoallvType(param.opType)) {
     176            0 :         CHK_PTR_NULL(executor.get());
     177            0 :         HCCL_INFO(
     178              :             "[AicpuCacheManager][PreProcessForCacheMiss] mark NeedAlltoallvCache for CollRunAlltoAllDirectFullmesh");
     179            0 :         CHK_RET(executor->MarkNeedAlltoallvCache());
     180              :     }
     181              : 
     182            0 :     return HCCL_SUCCESS;
     183              : }
     184              : 
     185            0 : HcclResult AicpuCacheManager::PostProcessForCacheMiss(
     186              :     const OpParam& param, std::unique_ptr<CollExecutorBase>& executor, Stream& mainStream,
     187              :     std::vector<Stream>& slaveStreams, void* dispatcherPtr, const HcclTopoInfo& topoinfo,
     188              :     const AlgOpContext& algContext, const HcclWorkflowMode workflowMode)
     189              : {
     190              :     // Cache miss会设置launch context to enable cache admission -> 需要清理launch context,
     191              :     // DispatcherAicpu不会再admit当前算子后续展开的SQE 注意: AicpuCacheManager下dispatcher一定是DispatcherAicpu
     192            0 :     CHK_RET((reinterpret_cast<DispatcherAiCpu*>(dispatcherPtr))->ClearLaunchContext());
     193              : 
     194              :     // 准备key
     195            0 :     OpUnfoldKey opUnfoldKey;
     196            0 :     CHK_RET(GetOpUnfoldKey(param, opUnfoldKey, topoinfo, algContext, workflowMode));
     197              : 
     198              :     // 校验cache entry (post cache miss前的orchestrate一定会add new cache entry)
     199            0 :     OpUnfoldCacheEntry* entryPtr = nullptr;
     200            0 :     CHK_PTR_NULL(opUnfoldCachePtr_);
     201            0 :     CHK_RET(opUnfoldCachePtr_->FindEntry(opUnfoldKey, &entryPtr));
     202            0 :     CHK_PTR_NULL(entryPtr); // Cache miss后刚刚admit的cache entry
     203              : 
     204              :     // 根据cache中的streamid计算是主流还是第几个从流
     205            0 :     HCCL_INFO(
     206              :         "[AicpuCacheManager][PostProcessForCacheMiss] calculate stream seq idxes for a newly-admitted entry of key %s",
     207              :         opUnfoldKey.GetKeyString().c_str());
     208            0 :     CHK_RET(entryPtr->CalcStreamSeqIdxes(mainStream, slaveStreams));
     209              : 
     210              :     // 针对alltoallv类算子, cache miss后处理
     211            0 :     if (IsAlltoallvType(param.opType)) {
     212              :         // 第一个需要cache的alltoallv类算子
     213              :         // 注意: 同一个通信域下, alltoallv类算子展开得到的hcclOffsetDstRanksMap是相同的, 所以只需要初始化一次
     214            0 :         if (!isInitAlltoallvMetadata_) {
     215              :             // 获得hcclOffset-dstRanks mapping
     216              :             // 注意: 只有当alltoallv的algName为"RunAlltoAllDirectFullmesh"时, 才会进入cache,
     217              :             // 所以使用的一定是CollRunAlltoAllDirectFullmesh executor
     218            0 :             CHK_PTR_NULL(executor.get());
     219            0 :             HCCL_INFO(
     220              :                 "[AicpuCacheManager][PostProcessForCacheMiss] get hcclOffset-dstRank mapping of key[%s] for "
     221              :                 "CollRunAlltoAllDirectFullmesh",
     222              :                 opUnfoldKey.GetKeyString().c_str());
     223            0 :             std::unordered_map<uint64_t, std::vector<uint32_t>> hcclOffsetDstRanksMap;
     224            0 :             CHK_RET(executor->GetHcclOffsetDstRanksMap(hcclOffsetDstRanksMap));
     225            0 :             for (std::unordered_map<uint64_t, std::vector<uint32_t>>::const_iterator mapIter
     226            0 :                  = hcclOffsetDstRanksMap.cbegin();
     227            0 :                  mapIter != hcclOffsetDstRanksMap.cend(); ++mapIter) {
     228            0 :                 alltoallvMetadata_.hcclOffsetDstRanksIdxMap.emplace(mapIter->first, std::make_pair(mapIter->second, 0));
     229              :             }
     230              : 
     231              :             // Dump hcclOffset-dstRanks mapping
     232            0 :             if (UNLIKELY(HcclCheckLogLevel(HCCL_LOG_INFO))) {
     233            0 :                 for (std::unordered_map<uint64_t, std::vector<uint32_t>>::const_iterator mapIter
     234            0 :                      = hcclOffsetDstRanksMap.cbegin();
     235            0 :                      mapIter != hcclOffsetDstRanksMap.cend(); ++mapIter) {
     236            0 :                     HCCL_INFO(
     237              :                         "[AicpuCacheManager][PostProcessForCacheMiss] hcclOffset[%llu]-dstRanks.size[%u]",
     238              :                         mapIter->first, mapIter->second.size());
     239            0 :                     for (uint32_t i = 0; i < mapIter->second.size(); ++i) {
     240            0 :                         HCCL_INFO(
     241              :                             "[AicpuCacheManager][PostProcessForCacheMiss] dstRanks[%u]: %u", i, mapIter->second[i]);
     242              :                     }
     243              :                 }
     244              :             }
     245              : 
     246            0 :             isInitAlltoallvMetadata_ = true;
     247            0 :         }
     248              : 
     249              :         // 根据hcclOffset-dstRank mapping更新PrepareIntraData case下dstRefreshInfo中的rank id
     250            0 :         CHK_RET(entryPtr->UpdateRefreshAddrInfoForAlltoallv(topoinfo.userRank, alltoallvMetadata_));
     251              :     }
     252              : 
     253            0 :     return HCCL_SUCCESS;
     254              : }
     255              : 
     256            0 : HcclResult AicpuCacheManager::ClearOpUnfoldCacheEntry(
     257              :     const std::string& algName, const OpParam& param, const AlgResourceResponse& algResource, const bool isDeviceMode,
     258              :     const HcclTopoInfo& topoinfo, std::unique_ptr<TopoMatcher>& topoMatcherPtr, const AlgOpContext& algContext,
     259              :     const HcclWorkflowMode workflowMode)
     260              : {
     261              :     // 清理当前aicpu算子对应的cache entry, 避免异常状态下, cache命中
     262            0 :     CHK_PTR_NULL(opUnfoldCachePtr_);
     263              : 
     264              :     // 判断是否需要cache
     265            0 :     bool needCache = false;
     266            0 :     CHK_RET(NeedOpUnfoldCache(
     267              :         algName, param, algResource, isDeviceMode, topoinfo, topoMatcherPtr, algContext, workflowMode, needCache));
     268            0 :     HCCL_INFO("[AicpuCacheManager][ClearOpUnfoldCacheEntry] needCache[%u]", needCache);
     269              : 
     270              :     // Cacheable算子
     271            0 :     if (needCache) {
     272              :         // 准备key
     273            0 :         OpUnfoldKey opUnfoldKey;
     274            0 :         CHK_RET(GetOpUnfoldKey(param, opUnfoldKey, topoinfo, algContext, workflowMode));
     275              : 
     276              :         // 清理cache entry if any
     277            0 :         HCCL_RUN_INFO(
     278              :             "[AicpuCacheManager][ClearOpUnfoldCacheEntry] try to clear cache entry for key[%s]",
     279              :             opUnfoldKey.GetKeyString().c_str());
     280            0 :         CHK_RET(opUnfoldCachePtr_->ClearEntry(opUnfoldKey));
     281              :     }
     282              : 
     283              :     // 针对alltoallv算子的缓存进行metadata清理
     284              :     // 注意: 即使当前故障算子不是alltoallv类算子, 由于NS快恢可能会重新分配资源 (例如hccl input / notify/ signal),
     285              :     //     为了保证alltoallvMetadata_的正确性, 必须重新计算并初始化alltoallvMetadata_
     286            0 :     CHK_RET(ClearMetadataForFirstAlltoallv());
     287              : 
     288              :     // 清理alltoallv类算子的cache entry
     289              :     // 注意: 为了保证NS快恢/重执行后, 必定进入alltoallvMetadata_重新计算和初始化的流程,
     290              :     // 需要清理与alltoallv类算子相关的entry,
     291              :     //     但对aicpu cache影响有限, 因为alltoallv类算子的cache entry数量有限 (只区分opType/isBigCount), 所以性能影响有限
     292            0 :     CHK_RET(opUnfoldCachePtr_->ClearEntryForAlltoallv());
     293              : 
     294            0 :     return HCCL_SUCCESS;
     295              : }
     296              : 
     297            0 : HcclResult AicpuCacheManager::ClearMetadataForFirstAlltoallv()
     298              : {
     299              :     // 确保故障/重执行后第一次可能被cache的alltoallv算子仍然会重新计算/初始化metadata
     300            0 :     isCalcAlltoallvMetadata_ = false;
     301            0 :     isInitAlltoallvMetadata_ = false;
     302            0 :     alltoallvMetadata_.Clear();
     303              : 
     304            0 :     return HCCL_SUCCESS;
     305              : }
     306              : 
     307            0 : HcclResult AicpuCacheManager::NeedOpUnfoldCache(
     308              :     const std::string& algName, const OpParam& param, const AlgResourceResponse& algResource, const bool isDeviceMode,
     309              :     const HcclTopoInfo& topoinfo, std::unique_ptr<TopoMatcher>& topoMatcherPtr, const AlgOpContext& algContext,
     310              :     const HcclWorkflowMode workflowMode, bool& needCache)
     311              : {
     312              :     // 初始化为不需要op-unfold cache
     313            0 :     needCache = false;
     314              : 
     315              :     // 检查cache容量
     316            0 :     if (opUnfoldCachePtr_->IsCacheFull()) {
     317            0 :         HCCL_INFO("[AicpuCacheManager][NeedOpUnfoldCache] cache is full, disable cache for current operator");
     318            0 :         return HCCL_SUCCESS;
     319              :     }
     320              : 
     321              :     // 校验环境变量
     322            0 :     if (param.aicpuCacheEnable == 0) {
     323            0 :         HCCL_INFO(
     324              :             "[AicpuCacheManager][NeedOpUnfoldCache] disable aicpu cache for aicpuCacheEnable[%u]",
     325              :             param.aicpuCacheEnable);
     326            0 :         return HCCL_SUCCESS;
     327              :     }
     328              : 
     329              :     // 屏蔽MC2算子
     330            0 :     if (isDeviceMode) {
     331            0 :         HCCL_INFO("[AicpuCacheManager][NeedOpUnfoldCache] MC2 op is not supported for operator unfolding cache");
     332            0 :         return HCCL_SUCCESS;
     333              :     }
     334            0 :     HCCL_INFO("[AicpuCacheManager][NeedOpUnfoldCache] device mode is not MC2 op");
     335              : 
     336              :     // 判断当前通信域是否使用RDMA (例如跨超通信域), 使用则不cache (因为RoCE队列的WQE不可见)
     337            0 :     const std::unordered_map<u32, bool>& isUsedRdmaMap = topoinfo.isUsedRdmaMap;
     338            0 :     for (std::unordered_map<u32, bool>::const_iterator map_iter = isUsedRdmaMap.cbegin();
     339            0 :          map_iter != isUsedRdmaMap.end(); ++map_iter) {
     340            0 :         if (map_iter->second) {
     341            0 :             HCCL_INFO(
     342              :                 "[AicpuCacheManager][NeedOpUnfoldCache] rank[%u] uses RDMA -> not supported for operator unfolding "
     343              :                 "cache",
     344              :                 map_iter->first);
     345            0 :             return HCCL_SUCCESS;
     346              :         }
     347              :     }
     348            0 :     HCCL_INFO("[AicpuCacheManager][NeedOpUnfoldCache] all ranks do not use RDMA");
     349              : 
     350              :     // 屏蔽inplace场景
     351            0 :     bool isInplace = false;
     352            0 :     CHK_RET(IsInplace(param, isInplace, topoinfo));
     353            0 :     if (isInplace) {
     354            0 :         HCCL_INFO("[AicpuCacheManager][NeedOpUnfoldCache] inplace case is not supported for operator unfolding cache");
     355            0 :         return HCCL_SUCCESS;
     356              :     }
     357              : 
     358              :     // 目前V类算子、batch类型算子、以及send/recv不考虑动态缓存 (使用白名单而非黑名单管理, 避免非预期算子进入cache机制)
     359              :     // 注意: 如果想要通过比较缓存刷新后的SQE与正常算子展开的SQE来debug, 可以将想要比较的算子从以下的cache白名单中移除,
     360              :     // 重新打包运行
     361            0 :     const HcclCMDType opType = param.opType;
     362            0 :     if (opType == HcclCMDType::HCCL_CMD_BROADCAST || opType == HcclCMDType::HCCL_CMD_REDUCE
     363            0 :         || opType == HcclCMDType::HCCL_CMD_ALLGATHER || opType == HcclCMDType::HCCL_CMD_REDUCE_SCATTER
     364            0 :         || opType == HcclCMDType::HCCL_CMD_ALLTOALL || opType == HcclCMDType::HCCL_CMD_SCATTER
     365            0 :         || opType == HcclCMDType::HCCL_CMD_ALLREDUCE) { // 非V类算子
     366            0 :         if (algName == "RunAlltoAllVStaged" || algName == "RunAlltoAllVFullMesh") {
     367            0 :             HCCL_INFO(
     368              :                 "[AicpuCacheManager][%s] algName[%s] is not supported for unfolding cache", __func__, algName.c_str());
     369            0 :             return HCCL_SUCCESS;
     370              :         }
     371            0 :         HCCL_INFO(
     372              :             "[AicpuCacheManager][NeedOpUnfoldCache] opType[%d] is supported for operator unfolding cache", opType);
     373            0 :         needCache = true;
     374            0 :     } else if (IsAlltoallvType(opType)) { // alltoallv类算子
     375              :         // 注意: 暂不支持alltoallv 图模式 / 存在强制单算子模式转换 (即图模式建链+单算子模式展开)
     376              :         // 原因: 这两个场景下, alltoallv每次执行会重新建链, 导致remote ranks' hccl buffer在本rank映射的虚拟地址发生变化;
     377              :         //     当前如果src是(local) user input, dst是local user output, 会当做LocalCopy根据recv offset进行src addr刷新;
     378              :         //     而图模式建链下, remote hccl input会作为remote user input传入cache, remote copy由原来的remote hccl input
     379              :         //     -> local user output变成(remote) user input -> local user output, 需要根据hccl offset进行src addr刷新
     380              :         // 结论: 由于现网下基本不存在alltoallv图模式调用, 暂对该场景不使能aicpu cache;
     381              :         //     如果需要支持, 应当在fullmesh算法中拦截srcRank-hcclOffset的映射, 并通过AlltoallvMetadata传入cache;
     382              :         //     识别到remote user input -> local user output时, 判断为图模式下的RemoteCopy;
     383              :         //     根据remote user input baseaddr (即remote hccl input在本rank映射的VA) + hccl offset更新src addr
     384            0 :         if (workflowMode == HcclWorkflowMode::HCCL_WORKFLOW_MODE_OPS_KERNEL_INFO_LIB || // 图模式
     385            0 :             param.aicpuCacheEnable > FORCE_OP_BASE_DELTA) { // 存在强制单算子模式转换 (即图模式建链+单算子模式展开)
     386            0 :             HCCL_INFO(
     387              :                 "[AicpuCacheManager][NeedOpUnfoldCache] graph mode[%u, %u > %u] is not supported for alltoallv's cache",
     388              :                 workflowMode, param.aicpuCacheEnable, FORCE_OP_BASE_DELTA);
     389            0 :             return HCCL_SUCCESS;
     390              :         }
     391              : 
     392              :         // 注意: 假设CollRunAlltoAllDirectFullmesh一定只使用AlltoAllVDirectFullMesh作为algTemp
     393            0 :         if (algName != "RunAlltoAllDirectFullmesh") {
     394            0 :             HCCL_INFO(
     395              :                 "[AicpuCacheManager][NeedOpUnfoldCache] algName[%s] is not supported for alltoallv's cache",
     396              :                 algName.c_str());
     397            0 :             return HCCL_SUCCESS;
     398              :         }
     399              : 
     400            0 :         if (!isCalcAlltoallvMetadata_) { // 当前通信域下第一次可能被cache的alltoallv, 需要计算相应metadata
     401            0 :             CHK_RET(CalcMetadataForFirstAlltoallv(algResource, isDeviceMode, topoinfo, topoMatcherPtr, algContext));
     402              : 
     403              :             // 后续不再重复计算alltoallv metadata
     404              :             // 注意: (i) 虽然alltoallvMetadata_中的相关mapping还未被初始化,
     405              :             // 但isCalcAlltoallvMetadata_只是为了避免重复计算部分metadata (ii) 参考CalcMetadataForFirstAlltoallv,
     406              :             // 例如sdmaDataBlockSize, hcclInputMemRanges, notifyIdRankRflagMap等 如果有故障发生: (i)
     407              :             // 发生在isCalcAlltoallvMetadata_ = true前,
     408              :             // 则ClearOpUnfoldCacheEntry会重新计算sdmaDataBlockSize来判断是否需要清理cache entry (ii) 发生在设置true后,
     409              :             // 即使在PostProcessForCacheMiss初始化相关mapping前, 也不影响清理时needCache的判断 (只依赖sdmaDataBlockSize)
     410            0 :             isCalcAlltoallvMetadata_ = true;
     411              :         }
     412              : 
     413              :         // 判断是否为小数据量的alltoallv类算子
     414            0 :         bool isSmallData = false;
     415            0 :         CHK_RET(IsSmallDataAlltoallv(param, isSmallData, topoinfo));
     416            0 :         if (!isSmallData) {
     417            0 :             HCCL_INFO(
     418              :                 "[AicpuCacheManager][NeedOpUnfoldCache] large-data alltoallv[%u] is not supported for operator "
     419              :                 "unfolding cache",
     420              :                 opType);
     421            0 :             return HCCL_SUCCESS;
     422              :         }
     423              : 
     424            0 :         HCCL_INFO(
     425              :             "[AicpuCacheManager][NeedOpUnfoldCache] small-data alltoallv[%u] is supported for operator unfolding cache",
     426              :             opType);
     427            0 :         needCache = true;
     428              :     } else {
     429            0 :         HCCL_INFO(
     430              :             "[AicpuCacheManager][NeedOpUnfoldCache] opType[%d] is not supported for operator unfolding cache", opType);
     431            0 :         return HCCL_SUCCESS;
     432              :     }
     433              : 
     434              :     // 到这里needCache应该为true (如果为false则已经提前返回了)
     435            0 :     CHK_PRT_RET(
     436              :         !needCache, HCCL_ERROR("[AicpuCacheManager][NeedOpUnfoldCache] needCache should be true"), HCCL_E_INTERNAL);
     437              : 
     438            0 :     return HCCL_SUCCESS;
     439              : }
     440              : 
     441            0 : HcclResult AicpuCacheManager::IsInplace(const OpParam& param, bool& isInplace, const HcclTopoInfo& topoinfo)
     442              : {
     443              :     // 准备input/output size
     444            0 :     HcclDataType sendType = HcclDataType::HCCL_DATA_TYPE_RESERVED;
     445            0 :     HcclDataType recvType = HcclDataType::HCCL_DATA_TYPE_RESERVED;
     446            0 :     uint64_t inputSize = 0;
     447            0 :     uint64_t outputSize = 0;
     448            0 :     CHK_RET(ParseOpParamForCache(param, sendType, recvType, inputSize, outputSize, topoinfo));
     449              :     UNUSED_PARAM(sendType);
     450              :     UNUSED_PARAM(recvType);
     451              : 
     452              :     // 注意: alltoall/alltoallv/alltoallvc可能存在inputSize/outputSize为0的情况, 导致不分配user input/output
     453              :     // 但会使用tinySendRecvMem_更新algResource.paramInput/OutputMem用于建链, 导致cache无法区分给定地址字段的地址类型
     454              :     // 参考aicpu_communicator.cc中的SetAlltoAllInputAndOutPutMem
     455            0 :     if (inputSize == 0 && outputSize == 0) {
     456            0 :         isInplace = true;
     457            0 :         HCCL_INFO(
     458              :             "[AicpuCacheManager][IsInplace] inputSize[%u] is overlapping with outputSize[%u]", inputSize, outputSize);
     459            0 :         return HCCL_SUCCESS;
     460              :     }
     461              : 
     462            0 :     if (inputSize == 0 || outputSize == 0) {
     463            0 :         isInplace = false;
     464            0 :         HCCL_INFO(
     465              :             "[AicpuCacheManager][IsInplace] inputSize[%u] is not overlapping with outputSize[%u]", inputSize,
     466              :             outputSize);
     467            0 :         return HCCL_SUCCESS;
     468              :     }
     469              : 
     470            0 :     const uint64_t inputStart = reinterpret_cast<uint64_t>(param.inputPtr);
     471            0 :     const uint64_t inputEnd = inputStart + inputSize - 1;
     472            0 :     const uint64_t outputStart = reinterpret_cast<uint64_t>(param.outputPtr);
     473            0 :     const uint64_t outputEnd = outputStart + outputSize - 1;
     474              : 
     475            0 :     if (inputStart <= outputEnd && outputStart <= inputEnd) {
     476            0 :         isInplace = true;
     477            0 :         HCCL_INFO(
     478              :             "[AicpuCacheManager][IsInplace] input[0x%016llx, 0x%016llx] is overlapping with output[0x%016llx, "
     479              :             "0x%016llx]",
     480              :             inputStart, inputEnd, outputStart, outputEnd);
     481              :     } else {
     482            0 :         isInplace = false;
     483            0 :         HCCL_INFO(
     484              :             "[AicpuCacheManager][IsInplace] input[0x%016llx, 0x%016llx] is not overlapping with output[0x%016llx, "
     485              :             "0x%016llx]",
     486              :             inputStart, inputEnd, outputStart, outputEnd);
     487              :     }
     488              : 
     489            0 :     return HCCL_SUCCESS;
     490              : }
     491              : 
     492            0 : bool AicpuCacheManager::IsAlltoallvType(const HcclCMDType opType)
     493              : {
     494              :     // alltoallv/alltoallvc只是对上接口不同, 实际算法编排相同, 均视为alltoallv类型的算子
     495            0 :     if (opType == HcclCMDType::HCCL_CMD_ALLTOALLV || opType == HcclCMDType::HCCL_CMD_ALLTOALLVC) {
     496            0 :         return true;
     497              :     }
     498            0 :     return false;
     499              : }
     500              : 
     501              : HcclResult
     502            0 : AicpuCacheManager::IsSmallDataAlltoallv(const OpParam& param, bool& isSmallData, const HcclTopoInfo& topoinfo)
     503              : {
     504              :     // 根据SDMA data block size判断是否需要cache
     505              :     // 参考coll_all_to_all_v_direct_fullmesh_executor.cc下的CollRunAlltoAllDirectFullmesh::GetLocalSendRecvInfoforAlltoallV
     506            0 :     const uint32_t rankSize = topoinfo.userRankSize;
     507            0 :     if (param.opType == HcclCMDType::HCCL_CMD_ALLTOALLV) { // alltoallv
     508            0 :         HCCL_INFO("[AicpuCacheManager][IsSmallDataAlltoallv] check %u send/recv counts", rankSize);
     509            0 :         for (uint32_t tmpRank = 0; tmpRank < rankSize; ++tmpRank) {
     510            0 :             const uint64_t curSendCounts = *(static_cast<const uint64_t*>(param.All2AllDataDes.sendCounts) + tmpRank);
     511            0 :             const uint64_t curSendLength = curSendCounts * SIZE_TABLE[param.All2AllDataDes.sendType];
     512              :             // 如果curSendLength超过SDMA data block size (即alltoallv需要切step), 或者超过HCCL_SDMA_MAX_COUNT_4GB
     513              :             // (即MemcpyAsync需要切split), 不做cache
     514            0 :             if (curSendLength > alltoallvMetadata_.sdmaDataBlockSize || curSendLength > HCCL_SDMA_MAX_COUNT_4GB) {
     515            0 :                 HCCL_INFO(
     516              :                     "[AicpuCacheManager][IsSmallDataAlltoallv] large-sdata alltoallv[%u]: userRank[%u] tmpRank[%u]"
     517              :                     "curSendLength[%u] sdmaDataBlockSize[%u] 4GB[%u]",
     518              :                     param.opType, topoinfo.userRank, tmpRank, curSendLength, alltoallvMetadata_.sdmaDataBlockSize,
     519              :                     HCCL_SDMA_MAX_COUNT_4GB);
     520            0 :                 isSmallData = false;
     521            0 :                 return HCCL_SUCCESS;
     522              :             }
     523              : 
     524            0 :             const uint64_t curRecvCounts = *(static_cast<const uint64_t*>(param.All2AllDataDes.recvCounts) + tmpRank);
     525            0 :             const uint64_t curRecvLength = curRecvCounts * SIZE_TABLE[param.All2AllDataDes.recvType];
     526              :             // 如果curRecvLength超过SDMA data block size (即alltoallv需要切step), 或者超过HCCL_SDMA_MAX_COUNT_4GB
     527              :             // (即MemcpyAsync需要切split), 不做cache
     528            0 :             if (curRecvLength > alltoallvMetadata_.sdmaDataBlockSize || curRecvLength > HCCL_SDMA_MAX_COUNT_4GB) {
     529            0 :                 HCCL_INFO(
     530              :                     "[AicpuCacheManager][IsSmallDataAlltoallv] large-rdata alltoallv[%u]: userRank[%u] tmpRank[%u]"
     531              :                     "curRecvLength[%u] sdmaDataBlockSize[%u] 4GB[%u]",
     532              :                     param.opType, topoinfo.userRank, tmpRank, curRecvLength, alltoallvMetadata_.sdmaDataBlockSize,
     533              :                     HCCL_SDMA_MAX_COUNT_4GB);
     534            0 :                 isSmallData = false;
     535            0 :                 return HCCL_SUCCESS;
     536              :             }
     537              :         }
     538            0 :     } else if (param.opType == HcclCMDType::HCCL_CMD_ALLTOALLVC) { // alltoallvc
     539            0 :         const uint32_t curRank = topoinfo.userRank;
     540            0 :         HCCL_INFO("[AicpuCacheManager][IsSmallDataAlltoallv] check %u-size sendCountMatrix", rankSize);
     541            0 :         for (uint32_t tmpRank = 0; tmpRank < rankSize; ++tmpRank) {
     542            0 :             const uint64_t curSendCounts
     543            0 :                 = *(static_cast<const u64*>(param.All2AllDataDes.sendCountMatrix) + curRank * rankSize
     544            0 :                     + tmpRank); // sendCountMatrix[curRank][tmpRank]
     545            0 :             const uint64_t curSendLength = curSendCounts * SIZE_TABLE[param.All2AllDataDes.sendType];
     546              :             // 如果curSendLength超过SDMA data block size (即alltoallv需要切step), 或者超过HCCL_SDMA_MAX_COUNT_4GB
     547              :             // (即MemcpyAsync需要切split), 不做cache
     548            0 :             if (curSendLength > alltoallvMetadata_.sdmaDataBlockSize || curSendLength > HCCL_SDMA_MAX_COUNT_4GB) {
     549            0 :                 HCCL_INFO(
     550              :                     "[AicpuCacheManager][IsSmallDataAlltoallv] large-sdata alltoallvc[%u]: userRank[%u] tmpRank[%u]"
     551              :                     "curSendLength[%u] sdmaDataBlockSize[%u] 4GB[%u]",
     552              :                     param.opType, curRank, tmpRank, curSendLength, alltoallvMetadata_.sdmaDataBlockSize,
     553              :                     HCCL_SDMA_MAX_COUNT_4GB);
     554            0 :                 isSmallData = false;
     555            0 :                 return HCCL_SUCCESS;
     556              :             }
     557              : 
     558            0 :             const uint64_t curRecvCounts
     559            0 :                 = *(static_cast<const u64*>(param.All2AllDataDes.sendCountMatrix) + tmpRank * topoinfo.userRankSize
     560            0 :                     + curRank); // sendCountMatrix[tmpRank][curRank]
     561            0 :             const uint64_t curRecvLength = curRecvCounts * SIZE_TABLE[param.All2AllDataDes.recvType];
     562              :             // 如果curRecvLength超过SDMA data block size (即alltoallv需要切step), 或者超过HCCL_SDMA_MAX_COUNT_4GB
     563              :             // (即MemcpyAsync需要切split), 不做cache
     564            0 :             if (curRecvLength > alltoallvMetadata_.sdmaDataBlockSize || curRecvLength > HCCL_SDMA_MAX_COUNT_4GB) {
     565            0 :                 HCCL_INFO(
     566              :                     "[AicpuCacheManager][IsSmallDataAlltoallv] large-rdata alltoallvc[%u]: userRank[%u] tmpRank[%u]"
     567              :                     "curRecvLength[%u] sdmaDataBlockSize[%u] 4GB[%u]",
     568              :                     param.opType, topoinfo.userRank, tmpRank, curRecvLength, alltoallvMetadata_.sdmaDataBlockSize,
     569              :                     HCCL_SDMA_MAX_COUNT_4GB);
     570            0 :                 isSmallData = false;
     571            0 :                 return HCCL_SUCCESS;
     572              :             }
     573              :         }
     574              :     } else {
     575            0 :         HCCL_ERROR("[AicpuCacheManager][IsSmallDataAlltoallv] invalid opType[%u] for alltoallv", param.opType);
     576            0 :         return HCCL_E_INTERNAL;
     577              :     }
     578              : 
     579            0 :     isSmallData = true;
     580            0 :     return HCCL_SUCCESS;
     581              : }
     582              : 
     583            0 : HcclResult AicpuCacheManager::CalcMetadataForFirstAlltoallv(
     584              :     const AlgResourceResponse& algResource, const bool isDeviceMode, const HcclTopoInfo& topoinfo,
     585              :     std::unique_ptr<TopoMatcher>& topoMatcherPtr, const AlgOpContext& algContext)
     586              : {
     587            0 :     alltoallvMetadata_.Clear();
     588            0 :     HCCL_INFO("[AicpuCacheManager][CalcMetadataForFirstAlltoallv] clear alltoallv metadata before calc.");
     589              : 
     590              :     // Part 1: 计算SDMA data block size, 用于根据数据量判断是否需要cache
     591              :     // 参考alltoallv_direct_fullmesh.cc下的AlltoAllVDirectFullMesh::Prepare (当前alltoallv算子只会使用direct full
     592              :     // mesh算法)
     593              : 
     594              :     // 获取local pod中的device数量
     595              :     // 参考coll_all_to_all_v_direct_fullmesh_executor.cc下的CollRunAlltoAllDirectFullmesh::GetLocalSDMAGroupInfo
     596            0 :     uint32_t devNumInlocalPod = 0;
     597            0 :     uint32_t rankIdxInPod = 0;
     598            0 :     const bool isA2MultiModule = topoinfo.deviceType == DevType::DEV_TYPE_910B && !topoinfo.isSingleMeshAggregation;
     599            0 :     if (topoMatcherPtr->GetExternalInputInterHccsDisable() || isA2MultiModule) {
     600            0 :         CHK_RET(topoMatcherPtr->GetLocalServerRankSize(topoinfo.userRank, devNumInlocalPod, rankIdxInPod));
     601              :     } else {
     602            0 :         CHK_RET(topoMatcherPtr->GetLocalSuperPodRankSize(topoinfo.userRank, devNumInlocalPod, rankIdxInPod));
     603              :     }
     604            0 :     CHK_PRT_RET(
     605              :         devNumInlocalPod == INVALID_VALUE_RANKSIZE,
     606              :         HCCL_ERROR("[AicpuCacheManager][CalcMetadataForFirstAlltoallv] get local superPod total ranksize failed."),
     607              :         HCCL_E_PARA);
     608              :     UNUSED_PARAM(rankIdxInPod);
     609              : 
     610              :     // 计算SDMA在alltoallv下的最大并发数量
     611            0 :     const uint32_t sdmaConcurrentNum = (devNumInlocalPod > ALLTOALLV_DIRECT_FULLMESH_SDMA_CONCURRENT_SIZE) ?
     612              :                                            (ALLTOALLV_DIRECT_FULLMESH_SDMA_CONCURRENT_SIZE) :
     613            0 :                                            (devNumInlocalPod);
     614              : 
     615              :     // 注意: MC2算子不会进入cache, 不需要根据MC2 stepSize调整sdmaConcurrentNum
     616            0 :     CHK_PRT_RET(
     617              :         algContext.mc2Handler.stepSize > 0,
     618              :         HCCL_ERROR(
     619              :             "[AicpuCacheManager][CalcMetadataForFirstAlltoallv] isDeviceMode[%u] mc2Handler.stepSize[%u]", isDeviceMode,
     620              :             algContext.mc2Handler.stepSize),
     621              :         HCCL_E_INTERNAL);
     622              : 
     623              :     // 计算SDMA data block大小
     624            0 :     constexpr uint32_t blockGroup = 2;
     625              :     alltoallvMetadata_.sdmaDataBlockSize
     626            0 :         = (algResource.cclInputMem.size() / std::max(1u, sdmaConcurrentNum * blockGroup));
     627              : 
     628              :     // 向下对齐到16k Byte
     629            0 :     if (alltoallvMetadata_.sdmaDataBlockSize > HCCL_MIN_SLICE_ALIGN_910B) {
     630              :         alltoallvMetadata_.sdmaDataBlockSize
     631            0 :             = (alltoallvMetadata_.sdmaDataBlockSize / HCCL_MIN_SLICE_ALIGN_910B) * HCCL_MIN_SLICE_ALIGN_910B;
     632              :     }
     633              : 
     634              :     // sdmaDataBlockSize应该大于0
     635            0 :     CHK_PRT_RET(
     636              :         alltoallvMetadata_.sdmaDataBlockSize == 0,
     637              :         HCCL_ERROR("[AicpuCacheManager][CalcMetadataForFirstAlltoallv] sdmaDataBlockSize is zero."), HCCL_E_INTERNAL);
     638              : 
     639            0 :     HCCL_INFO(
     640              :         "[AicpuCacheManager][CalcMetadataForFirstAlltoallv] first alltoallv, devNumInlocalPod[%u],"
     641              :         "sdmaConcurrentNum[%u] cclInputSize[%u] sdmaDataBlockSize[%u]",
     642              :         devNumInlocalPod, sdmaConcurrentNum, algResource.cclInputMem.size(), alltoallvMetadata_.sdmaDataBlockSize);
     643              : 
     644              :     // Part 2: 计算每个rank的HCCL input buffer memory range
     645              :     // 参考alltoallv_direct_fullmesh.cc下的SDMAwithRemoteRankAndNotifyEnd,
     646              :     // coll_all_to_all_v_direct_fullmesh_executor.cc下的KernelRun,
     647              :     //     和coll_native_executor_base.cc下的GetSubCommInfo
     648              : 
     649              :     // 初始化hcclInputMemRanges
     650            0 :     const uint32_t rankSize = topoinfo.userRankSize;
     651            0 :     HCCL_INFO(
     652              :         "[AicpuCacheManager][CalcMetadataForFirstAlltoallv] prepare %u hccl input memory ranges for op-unfold cache",
     653              :         rankSize);
     654            0 :     std::vector<OpUnfoldMemRange>& hcclInputMemRanges = alltoallvMetadata_.hcclInputMemRanges;
     655            0 :     hcclInputMemRanges.resize(rankSize);
     656              : 
     657              :     // 准备hccl input size
     658              :     // 来自于cclInputBuffer_ (size来自于HcclOpResParam commParam.winSize, 单位是bytes)
     659            0 :     const uint64_t hcclInputSize = algResource.cclInputMem.size();
     660              : 
     661              :     // 设置当前rank的hccl input memory range
     662            0 :     const uint32_t curRank = topoinfo.userRank; // NOTE: 不应该使用param.srcRank (某些算子始终为0)
     663            0 :     CHK_PRT_RET(
     664              :         curRank >= rankSize,
     665              :         HCCL_ERROR(
     666              :             "[AicpuCacheManager][CalcMetadataForFirstAlltoallv] invalid curRank %u >= rankSize %u", curRank, rankSize),
     667              :         HCCL_E_INTERNAL);
     668            0 :     OpUnfoldMemRange& curHcclInputMemRange = hcclInputMemRanges[curRank];
     669            0 :     curHcclInputMemRange.isValid = true;
     670            0 :     curHcclInputMemRange.baseAddr = reinterpret_cast<uint64_t>(algResource.cclInputMem.ptr());
     671              :     curHcclInputMemRange.memSize
     672            0 :         = hcclInputSize; // NOTE: 不应该使用param.inputSize (user memory input size, 且alltoall类始终为0)
     673              : 
     674              :     // 设置其他rank的hccl input memory range
     675            0 :     const std::vector<LINK>& links = algResource.opTransportResponse[COMM_COMBINE_ORDER][COMM_INDEX_0].links;
     676            0 :     CHK_PRT_RET(
     677              :         links.size() != rankSize,
     678              :         HCCL_ERROR(
     679              :             "[AicpuCacheManager][CalcMetadataForFirstAlltoallv] links.size[%u] != rankSize[%u]", links.size(),
     680              :             rankSize),
     681              :         HCCL_E_INTERNAL);
     682            0 :     for (uint32_t tmpRank = 0; tmpRank < rankSize; ++tmpRank) {
     683            0 :         if (tmpRank == curRank) {
     684            0 :             continue;
     685              :         }
     686              : 
     687              :         // 获取curRank与tmpRank之间的LINK
     688            0 :         const LINK& intraNeighboorTransport = links[tmpRank];
     689            0 :         CHK_PTR_NULL(intraNeighboorTransport);
     690              : 
     691              :         // 获取tmpRank的hccl input memory baseaddr
     692            0 :         void* tmpHcclInputBaseAddr = nullptr;
     693            0 :         CHK_RET(intraNeighboorTransport->GetRemoteMem(UserMemType::INPUT_MEM, &tmpHcclInputBaseAddr));
     694            0 :         CHK_PTR_NULL(tmpHcclInputBaseAddr);
     695              : 
     696              :         // 设置tmpRank对应的hccl input memory range
     697            0 :         OpUnfoldMemRange& tmpHcclInputMemRange = hcclInputMemRanges[tmpRank];
     698            0 :         tmpHcclInputMemRange.isValid = true;
     699            0 :         tmpHcclInputMemRange.baseAddr = reinterpret_cast<uint64_t>(tmpHcclInputBaseAddr);
     700            0 :         tmpHcclInputMemRange.memSize = hcclInputSize;
     701              :     }
     702              : 
     703              :     // 打印debug信息
     704            0 :     if (UNLIKELY(HcclCheckLogLevel(HCCL_LOG_INFO))) {
     705            0 :         for (size_t rankId = 0; rankId < hcclInputMemRanges.size(); ++rankId) {
     706            0 :             HCCL_INFO(
     707              :                 "[AicpuCacheManager][CalcMetadataForFirstAlltoallv] hcclInputMemRanges[%u] isValid: %d,"
     708              :                 "baseAddr: 0x%016llx, memSize: %llu",
     709              :                 rankId, hcclInputMemRanges[rankId].isValid, hcclInputMemRanges[rankId].baseAddr,
     710              :                 hcclInputMemRanges[rankId].memSize);
     711              :         }
     712              :     }
     713              : 
     714              :     // Part 3: 计算notifyId与remoteRank间的映射
     715              : 
     716            0 :     HCCL_INFO(
     717              :         "[AicpuCacheManager][CalcMetadataForFirstAlltoallv] prepare %u notify info for op-unfold cache", rankSize - 1);
     718            0 :     for (uint32_t tmpRank = 0; tmpRank < rankSize; ++tmpRank) {
     719            0 :         if (tmpRank == curRank) {
     720            0 :             continue;
     721              :         }
     722              : 
     723              :         // 获取curRank与tmpRank之间的LINK
     724            0 :         const LINK& intraNeighboorTransport = links[tmpRank];
     725            0 :         CHK_PTR_NULL(intraNeighboorTransport);
     726              : 
     727              :         // 获取RxAck相关的NotifyId (与recv count相关)
     728              :         HcclSignalInfo recvNotifyInfo;
     729            0 :         bool recvIsValid = false;
     730            0 :         CHK_RET(intraNeighboorTransport->GetSpecificNotify(recvNotifyInfo, recvIsValid, "localSendDone"));
     731            0 :         CHK_PRT_RET(
     732              :             !recvIsValid, HCCL_ERROR("[AicpuCacheManager][CalcMetadataForFirstAlltoallv] invalid localSendDoneNotify_"),
     733              :             HCCL_E_INTERNAL);
     734            0 :         const uint32_t recvNotifyId = static_cast<uint32_t>(recvNotifyInfo.resId);
     735            0 :         alltoallvMetadata_.notifyIdRankRflagMap.emplace(recvNotifyId, std::make_pair(tmpRank, true));
     736              : 
     737              :         // 获取RxDataSignal相关的NotifyId (与send count相关)
     738              :         HcclSignalInfo sendNotifyInfo;
     739            0 :         bool sendIsValid = false;
     740            0 :         CHK_RET(intraNeighboorTransport->GetSpecificNotify(sendNotifyInfo, sendIsValid, "localSendReady"));
     741            0 :         CHK_PRT_RET(
     742              :             !sendIsValid,
     743              :             HCCL_ERROR("[AicpuCacheManager][CalcMetadataForFirstAlltoallv] invalid localSendReadyNotify_"),
     744              :             HCCL_E_INTERNAL);
     745            0 :         const uint32_t sendNotifyId = static_cast<uint32_t>(sendNotifyInfo.resId);
     746            0 :         alltoallvMetadata_.notifyIdRankRflagMap.emplace(sendNotifyId, std::make_pair(tmpRank, false));
     747              :     }
     748              : 
     749              :     // Part 4: 计算signalAddr与remoteRank的映射
     750              : 
     751            0 :     HCCL_INFO(
     752              :         "[AicpuCacheManager][CalcMetadataForFirstAlltoallv] prepare %u signal info for op-unfold cache", rankSize - 1);
     753            0 :     for (uint32_t tmpRank = 0; tmpRank < rankSize; ++tmpRank) {
     754            0 :         if (tmpRank == curRank) {
     755            0 :             continue;
     756              :         }
     757              : 
     758              :         // 获取curRank与tmpRank之间的LINK
     759            0 :         const LINK& intraNeighboorTransport = links[tmpRank];
     760            0 :         CHK_PTR_NULL(intraNeighboorTransport);
     761              : 
     762              :         // 获取TxDataSignal相关的SignalAddr (与recv count相关)
     763              :         HcclSignalInfo recvNotifyInfo;
     764            0 :         bool recvIsValid = false;
     765            0 :         CHK_RET(intraNeighboorTransport->GetSpecificNotify(recvNotifyInfo, recvIsValid, "remoteSendReady"));
     766            0 :         CHK_PRT_RET(
     767              :             !recvIsValid,
     768              :             HCCL_ERROR("[AicpuCacheManager][CalcMetadataForFirstAlltoallv] invalid remoteSendReadyNotify_"),
     769              :             HCCL_E_INTERNAL);
     770            0 :         const uint64_t recvSignalAddr = recvNotifyInfo.addr;
     771            0 :         alltoallvMetadata_.signalAddrRankRflagMap.emplace(recvSignalAddr, std::make_pair(tmpRank, true));
     772              : 
     773              :         // 获取TxAck相关的SignalAddr (与send count相关)
     774              :         HcclSignalInfo sendNotifyInfo;
     775            0 :         bool sendIsValid = false;
     776            0 :         CHK_RET(intraNeighboorTransport->GetSpecificNotify(sendNotifyInfo, sendIsValid, "remoteSendDone"));
     777            0 :         CHK_PRT_RET(
     778              :             !sendIsValid,
     779              :             HCCL_ERROR("[AicpuCacheManager][CalcMetadataForFirstAlltoallv] invalid remoteSendDoneNotify_"),
     780              :             HCCL_E_INTERNAL);
     781            0 :         const uint64_t sendSignalAddr = sendNotifyInfo.addr;
     782            0 :         alltoallvMetadata_.signalAddrRankRflagMap.emplace(sendSignalAddr, std::make_pair(tmpRank, false));
     783              :     }
     784              : 
     785            0 :     return HCCL_SUCCESS;
     786              : }
     787              : 
     788            0 : HcclResult AicpuCacheManager::GetOpUnfoldKey(
     789              :     const OpParam& param, OpUnfoldKey& key, const HcclTopoInfo& topoinfo, const AlgOpContext& algContext,
     790              :     const HcclWorkflowMode workflowMode)
     791              : {
     792              :     // 注意: 由于GetOpUnfoldKey前已经做过NeedOpUnfoldCache检查, 这里不再做重复检验
     793              : 
     794              :     // 准备sendType和inputSize
     795              :     // 注意: 如果是alltoallv类算子, sendType设置为RESERVED, inputSize设置为0, 保证即使dataType, sendCounts,
     796              :     // recvCounts发生变化, 仍然能够缓存命中
     797            0 :     HcclDataType sendType = HcclDataType::HCCL_DATA_TYPE_RESERVED;
     798            0 :     uint64_t inputSize = 0;
     799            0 :     if (!IsAlltoallvType(param.opType)) { // 非alltoallv类算子, 需要根据sendType和inputSize生成不同的key
     800            0 :         HcclDataType recvType = HcclDataType::HCCL_DATA_TYPE_RESERVED;
     801            0 :         uint64_t outputSize = 0;
     802            0 :         CHK_RET(ParseOpParamForCache(param, sendType, recvType, inputSize, outputSize, topoinfo));
     803              :         UNUSED_PARAM(recvType);
     804              :         UNUSED_PARAM(outputSize);
     805              :     } else { // alltoallv类算子, 需要根据isBigCount生成不同的key, 决定SQE编排是否需要并发
     806            0 :         bool isBigCountForAlltoallv = false;
     807            0 :         CHK_RET(IsBigCountForAlltoallv(param, topoinfo, isBigCountForAlltoallv));
     808            0 :         if (isBigCountForAlltoallv) {
     809            0 :             inputSize = 1;
     810              :         } else {
     811            0 :             inputSize = 0;
     812              :         }
     813              :     }
     814              : 
     815              :     // 准备root: 仅 scatter / broadcast / reduce 三类算子的SQE模板会随root变化, 必须纳入key
     816              :     // 注意: 其他算子 (allgather/allreduce/alltoall/...) 在不同root下SQE模板相同, 固定为0即可
     817            0 :     uint32_t root = 0;
     818            0 :     if (param.opType == HcclCMDType::HCCL_CMD_SCATTER || param.opType == HcclCMDType::HCCL_CMD_BROADCAST
     819            0 :         || param.opType == HcclCMDType::HCCL_CMD_REDUCE) {
     820            0 :         root = param.root;
     821              :     }
     822              : 
     823              :     // 设置key for op-unfold cache
     824            0 :     CHK_RET(key.Init(
     825              :         param.opType, sendType, param.reduceType, param.isZeroCopy, isSymmetricMemory_, inputSize,
     826              :         algContext.opRetryHandler.isInplacePreSync, workflowMode, param.isCapture, root));
     827              : 
     828            0 :     return HCCL_SUCCESS;
     829              : }
     830              : 
     831              : HcclResult
     832            0 : AicpuCacheManager::IsBigCountForAlltoallv(const OpParam& param, const HcclTopoInfo& topoinfo, bool& isBigCount)
     833              : {
     834            0 :     isBigCount = false;
     835            0 :     if (IsAlltoallvType(param.opType)) { // alltoallv类算子
     836              :         // 计算maxSendCount
     837              :         // 参考coll_all_to_all_v_direct_fullmesh_executor.cc中的GetLocalSendRecvInfoforAlltoallV
     838            0 :         const uint32_t curRank = topoinfo.userRank;
     839            0 :         const uint32_t rankSize = topoinfo.userRankSize;
     840            0 :         uint64_t maxSendCount = 0;
     841            0 :         for (size_t dstRank = 0; dstRank < rankSize; ++dstRank) {
     842              :             // 获得curRank -> dstRank的sendCount
     843            0 :             uint64_t curSendCount = 0;
     844            0 :             if (param.opType == HcclCMDType::HCCL_CMD_ALLTOALLV) { // alltoallv
     845            0 :                 curSendCount = *(static_cast<const uint64_t*>(param.All2AllDataDes.sendCounts) + dstRank);
     846            0 :             } else if (param.opType == HcclCMDType::HCCL_CMD_ALLTOALLVC) { // alltoallvc
     847              :                 curSendCount
     848            0 :                     = *(static_cast<const uint64_t*>(param.All2AllDataDes.sendCountMatrix) + curRank * rankSize
     849            0 :                         + dstRank); // sendCountMatrix[curRank][dstRank]
     850              :             } else {
     851            0 :                 HCCL_ERROR(
     852              :                     "[AicpuCacheManager][IsBigCountForAlltoallv] invalid opType[%u] for alltoallv", param.opType);
     853            0 :                 return HCCL_E_INTERNAL;
     854              :             }
     855              : 
     856              :             // 更新maxSendCount
     857            0 :             if (curSendCount > maxSendCount) {
     858            0 :                 maxSendCount = curSendCount;
     859              :             }
     860              :         }
     861              : 
     862              :         // 参考alltoallv_direct_fullmesh.cc中的Prepare()
     863            0 :         uint64_t maxSendLen = maxSendCount * SIZE_TABLE[param.All2AllDataDes.sendType];
     864            0 :         isBigCount = (maxSendLen > ALLTOALLV_DIRECT_FULLMESH_BIG_SIZE) ? true : false;
     865              : 
     866            0 :         HCCL_INFO(
     867              :             "[AicpuCacheManager][IsBigCountForAlltoallv] maxSendCount[%llu] maxSendLen[%llu] isBigCount[%u]",
     868              :             maxSendCount, maxSendLen, isBigCount);
     869              :     }
     870              : 
     871            0 :     return HCCL_SUCCESS;
     872              : }
     873              : 
     874            0 : HcclResult AicpuCacheManager::PrepareUserMemRanges(
     875              :     const OpParam& param, const AlgResourceResponse& algResource, std::vector<OpUnfoldMemRange>& userInputMemRanges,
     876              :     std::vector<OpUnfoldMemRange>& userOutputMemRanges, const HcclTopoInfo& topoinfo,
     877              :     std::shared_ptr<AicpuZeroCopyExchanger>& zeroCopyExchangerPtr, const HcclWorkflowMode workflowMode,
     878              :     const DeviceMem& tinySendRecvMem)
     879              : {
     880              :     // 注意: 由于PrepareUserMemRanges前已经做过NeedOpUnfoldCache检查, 这里不再做重复检验
     881              : 
     882            0 :     const uint32_t rankSize = topoinfo.userRankSize;
     883            0 :     HCCL_INFO(
     884              :         "[AicpuCacheManager][PrepareUserMemRanges] prepare %u user input/output memory ranges for op-unfold cache",
     885              :         rankSize);
     886              : 
     887              :     // 准备memory ranges
     888            0 :     userInputMemRanges.resize(rankSize);
     889            0 :     userOutputMemRanges.resize(rankSize);
     890              : 
     891              :     // 准备input/output size
     892            0 :     HcclDataType sendType = HcclDataType::HCCL_DATA_TYPE_RESERVED;
     893            0 :     HcclDataType recvType = HcclDataType::HCCL_DATA_TYPE_RESERVED;
     894            0 :     uint64_t inputSize = 0;
     895            0 :     uint64_t outputSize = 0;
     896            0 :     CHK_RET(ParseOpParamForCache(param, sendType, recvType, inputSize, outputSize, topoinfo));
     897              : 
     898              :     // 校验input/output size (应该在NeedOpUnfoldCache中被IsInplace拦截)
     899            0 :     CHK_PRT_RET(
     900              :         inputSize == 0 && outputSize == 0,
     901              :         HCCL_ERROR("[AicpuCacheManager][PrepareUserMemRanges] inputSize[%llu] outputSize[%llu]", inputSize, outputSize),
     902              :         HCCL_E_INTERNAL);
     903              : 
     904              :     // 当前只有alltoall/alltoallv/alltoallvc才会存在inputSize/outputSize为0的情况
     905              :     // 注意: 对于alltoall算子, outputSize一定等于inputSize, 所以不会存在inputSize/outputSize之一为0的情况
     906              :     // -> 这里其实只考虑alltoallv/alltoallvc
     907              :     // 参考aicpu_communicator.cc中的SetAlltoAllInputAndOutPutMem
     908            0 :     const HcclCMDType opType = param.opType;
     909            0 :     CHK_PRT_RET(
     910              :         (inputSize == 0 || outputSize == 0) && opType != HCCL_CMD_ALLTOALL && opType != HCCL_CMD_ALLTOALLV
     911              :             && opType != HCCL_CMD_ALLTOALLVC,
     912              :         HCCL_ERROR(
     913              :             "[AicpuCacheManager][PrepareUserMemRanges] opType[%u] inputSize[%llu] outputSize[%llu]", opType, inputSize,
     914              :             outputSize),
     915              :         HCCL_E_INTERNAL);
     916              : 
     917              :     // 设置当前rank的input/output usermem addr
     918            0 :     const uint32_t curRank = topoinfo.userRank; // NOTE: 不应该使用param.srcRank (某些算子始终为0)
     919            0 :     CHK_PRT_RET(
     920              :         curRank >= rankSize,
     921              :         HCCL_ERROR("[AicpuCacheManager][PrepareUserMemRanges] invalid curRank %u >= rankSize %u", curRank, rankSize),
     922              :         HCCL_E_INTERNAL);
     923            0 :     HCCL_INFO("[AicpuCacheManager][PrepareUserMemRanges] prepare user memory range of current rank %u", curRank);
     924            0 :     OpUnfoldMemRange& curUserInputMemRange = userInputMemRanges[curRank];
     925            0 :     curUserInputMemRange.isValid = true;
     926            0 :     if (inputSize == 0) { // 处理alltoallv/alltoallvc的corner case
     927            0 :         HCCL_INFO(
     928              :             "[AicpuCacheManager][PrepareUserMemRanges] use tinySendRecvMem[0x%016llx, %llu]"
     929              :             "as local user input for opType[%u]",
     930              :             tinySendRecvMem.ptr(), tinySendRecvMem.size(), opType);
     931            0 :         curUserInputMemRange.baseAddr = reinterpret_cast<uint64_t>(tinySendRecvMem.ptr());
     932            0 :         curUserInputMemRange.memSize = tinySendRecvMem.size();
     933              :     } else {
     934            0 :         curUserInputMemRange.baseAddr = reinterpret_cast<uint64_t>(param.inputPtr);
     935            0 :         curUserInputMemRange.memSize = inputSize; // NOTE: 不应该使用param.inputSize (alltoall类始终为0)
     936              :     }
     937            0 :     OpUnfoldMemRange& curUserOutputMemRange = userOutputMemRanges[curRank];
     938            0 :     curUserOutputMemRange.isValid = true;
     939            0 :     if (outputSize == 0) { // 处理alltoallv/alltoallvc的corner case
     940            0 :         HCCL_INFO(
     941              :             "[AicpuCacheManager][PrepareUserMemRanges] use tinySendRecvMem[0x%016llx, %llu]"
     942              :             "as local user output for opType[%u]",
     943              :             tinySendRecvMem.ptr(), tinySendRecvMem.size(), opType);
     944            0 :         curUserOutputMemRange.baseAddr = reinterpret_cast<uint64_t>(tinySendRecvMem.ptr());
     945            0 :         curUserOutputMemRange.memSize = tinySendRecvMem.size();
     946              :     } else {
     947            0 :         curUserOutputMemRange.baseAddr = reinterpret_cast<uint64_t>(param.outputPtr);
     948            0 :         curUserOutputMemRange.memSize = outputSize; // NOTE: 不应该使用param.outputSize (alltoall类始终为0)
     949              :     }
     950              : 
     951              :     // 针对zero copy, 设置remote rank的input/output usermem addr
     952            0 :     if (param.isZeroCopy) {
     953              :         // 注意: 只有非V类算子可能使用zero copy (因此假设remote ranks' input/output size与local rank相同)
     954              :         // 注意: 而V类算子一定是buffer copy, 只会存在local user/hccl <-> remote hccl之间的搬运
     955              :         // (否则PrepareRemoteUserMemRanges需要额外的输入作为remote ranks' input/output size)
     956            0 :         CHK_PRT_RET(
     957              :             opType == HCCL_CMD_ALLTOALLV || opType == HCCL_CMD_ALLTOALLVC || opType == HCCL_CMD_ALLGATHER_V
     958              :                 || opType == HCCL_CMD_REDUCE_SCATTER_V || opType == HCCL_CMD_HALF_ALLTOALLV,
     959              :             HCCL_ERROR("[AicpuCacheManager][PrepareUserMemRanges] opType[%u] should not use zero copy", opType),
     960              :             HCCL_E_INTERNAL);
     961              : 
     962              :         // 直接传入local rank's input/output size用于remote ranks' memory ranges
     963            0 :         HCCL_INFO("[AicpuCacheManager][PrepareUserMemRanges] prepare user memory ranges of other remote ranks");
     964            0 :         CHK_PTR_NULL(zeroCopyExchangerPtr.get());
     965            0 :         CHK_RET(zeroCopyExchangerPtr->PrepareRemoteUserMemRanges(
     966              :             inputSize, outputSize, userInputMemRanges, userOutputMemRanges));
     967            0 :     } else if (
     968            0 :         isSymmetricMemory_
     969            0 :         || // 对称内存场景:remote ranks 的 user mem 需从 opTransportResponse 的 links 中获取
     970              :            // PrepareSymmetricMemory 已在 ExecOp 中通过 HcclSymWinGetPeerPointer 刷新各 link 的 remote addr
     971            0 :         workflowMode == HcclWorkflowMode::HCCL_WORKFLOW_MODE_OPS_KERNEL_INFO_LIB || // 图模式
     972            0 :         param.aicpuCacheEnable > FORCE_OP_BASE_DELTA) { // 存在强制单算子模式转换 (即图模式建链+单算子模式展开)
     973            0 :         HCCL_INFO("[AicpuCacheManager][PrepareUserMemRanges] check transport resource for potential user memory of "
     974              :                   "remote ranks");
     975              : 
     976              :         // 遍历所有transport信息, 更新remote ranks' user input/output memory ranges
     977            0 :         for (size_t planeIdx = 0; planeIdx < algResource.opTransportResponse.size(); ++planeIdx) {
     978            0 :             const LevelNSubCommTransport& subCommTransport = algResource.opTransportResponse[planeIdx];
     979            0 :             for (size_t commIdx = 0; commIdx < subCommTransport.size(); ++commIdx) {
     980            0 :                 const SingleSubCommTransport& commTransport = subCommTransport[commIdx];
     981              : 
     982              :                 // 注意: 假设SingleSubCommTransport中的transportRequests和links是一一对应的
     983            0 :                 const std::vector<TransportRequest>& transportRequests = commTransport.transportRequests;
     984            0 :                 const std::vector<LINK>& links = commTransport.links;
     985            0 :                 HCCL_INFO(
     986              :                     "[AicpuCacheManager][PrepareUserMemRanges] planeIdx[%u] commIdx[%u] links.size[%u]", planeIdx,
     987              :                     commIdx, links.size());
     988            0 :                 CHK_PRT_RET(
     989              :                     transportRequests.size() != links.size(),
     990              :                     HCCL_ERROR(
     991              :                         "[AicpuCacheManager][PrepareUserMemRanges] transportRequests.size[%u] != links.size[%u]",
     992              :                         transportRequests.size(), links.size()),
     993              :                     HCCL_E_INTERNAL);
     994              : 
     995              :                 // 遍历每个remote rank对应的link信息
     996            0 :                 for (size_t reqIdx = 0; reqIdx < transportRequests.size(); ++reqIdx) {
     997            0 :                     const TransportRequest& curReq = transportRequests[reqIdx];
     998            0 :                     if (curReq.isValid) {
     999            0 :                         if (curReq.remoteUserRank == curRank) { // 本rank无需从link获取user memory range
    1000            0 :                             continue;
    1001            0 :                         } else if (curReq.remoteUserRank == INVALID_VALUE_RANKID) { // 本rank无需从link获取user memory
    1002              :                                                                                     // range
    1003            0 :                             continue;
    1004              :                         }
    1005              : 
    1006            0 :                         CHK_PRT_RET(
    1007              :                             curReq.remoteUserRank >= rankSize,
    1008              :                             HCCL_ERROR(
    1009              :                                 "[AicpuCacheManager][PrepareUserMemRanges] invalid remoteRank %u >= rankSize %u",
    1010              :                                 curReq.remoteUserRank, rankSize),
    1011              :                             HCCL_E_INTERNAL);
    1012              : 
    1013              :                         // 获取curRank与remoteRank之间的LINK
    1014            0 :                         const LINK& curLink = links[reqIdx];
    1015            0 :                         CHK_PTR_NULL(curLink);
    1016              : 
    1017              :                         // 获取user input memory range if any
    1018            0 :                         if (curReq.inputMemType == TransportMemType::PARAM_INPUT
    1019            0 :                             || curReq.inputMemType == TransportMemType::CCL_INPUT) {
    1020              :                             // 获取remoteRank的user input memory baseaddr
    1021            0 :                             void* remoteUserInputBaseAddr = nullptr;
    1022            0 :                             CHK_RET(curLink->GetRemoteMem(UserMemType::INPUT_MEM, &remoteUserInputBaseAddr));
    1023            0 :                             CHK_PTR_NULL(remoteUserInputBaseAddr);
    1024              : 
    1025            0 :                             HCCL_INFO(
    1026              :                                 "[AicpuCacheManager][PrepareUserMemRanges] prepare user input of remoteRank[%u] for"
    1027              :                                 "graph mode; baseAddr[0x%016llx]",
    1028              :                                 curReq.remoteUserRank, remoteUserInputBaseAddr);
    1029              : 
    1030              :                             // 设置remoteRank对应的user input memory range
    1031            0 :                             OpUnfoldMemRange& remoteUserInputMemRange = userInputMemRanges[curReq.remoteUserRank];
    1032            0 :                             remoteUserInputMemRange.isValid = true;
    1033            0 :                             remoteUserInputMemRange.baseAddr = reinterpret_cast<uint64_t>(remoteUserInputBaseAddr);
    1034            0 :                             if (curReq.inputMemType == TransportMemType::PARAM_INPUT) { // user input
    1035            0 :                                 remoteUserInputMemRange.memSize = inputSize;
    1036            0 :                             } else if (curReq.inputMemType == TransportMemType::CCL_INPUT) { // hccl input
    1037            0 :                                 remoteUserInputMemRange.memSize = algResource.cclInputMem.size();
    1038              :                             } else {
    1039            0 :                                 HCCL_ERROR(
    1040              :                                     "[AicpuCacheManager][PrepareUserMemRanges] invalid curReq.inputMemType[%u]",
    1041              :                                     curReq.inputMemType);
    1042            0 :                                 return HCCL_E_INTERNAL;
    1043              :                             }
    1044              :                         }
    1045              : 
    1046              :                         // 获取user output memory range if any
    1047            0 :                         if (curReq.outputMemType == TransportMemType::PARAM_OUTPUT
    1048            0 :                             || curReq.outputMemType == TransportMemType::CCL_OUTPUT) {
    1049              :                             // 获取remoteRank的user output memory baseaddr
    1050            0 :                             void* remoteUserOutputBaseAddr = nullptr;
    1051            0 :                             CHK_RET(curLink->GetRemoteMem(UserMemType::OUTPUT_MEM, &remoteUserOutputBaseAddr));
    1052            0 :                             CHK_PTR_NULL(remoteUserOutputBaseAddr);
    1053              : 
    1054            0 :                             HCCL_INFO(
    1055              :                                 "[AicpuCacheManager][PrepareUserMemRanges] prepare user output of remoteRank[%u] for"
    1056              :                                 "graph mode; baseAddr[0x%016llx]",
    1057              :                                 curReq.remoteUserRank, remoteUserOutputBaseAddr);
    1058              : 
    1059              :                             // 设置remoteRank对应的user output memory range
    1060            0 :                             OpUnfoldMemRange& remoteUserOutputMemRange = userOutputMemRanges[curReq.remoteUserRank];
    1061            0 :                             remoteUserOutputMemRange.isValid = true;
    1062            0 :                             remoteUserOutputMemRange.baseAddr = reinterpret_cast<uint64_t>(remoteUserOutputBaseAddr);
    1063            0 :                             if (curReq.outputMemType == TransportMemType::PARAM_OUTPUT) { // user output
    1064            0 :                                 remoteUserOutputMemRange.memSize = outputSize;
    1065            0 :                             } else if (curReq.outputMemType == TransportMemType::CCL_OUTPUT) { // hccl output
    1066            0 :                                 remoteUserOutputMemRange.memSize = algResource.cclOutputMem.size();
    1067              :                             } else {
    1068            0 :                                 HCCL_ERROR(
    1069              :                                     "[AicpuCacheManager][PrepareUserMemRanges] invalid curReq.outputMemType[%u]",
    1070              :                                     curReq.outputMemType);
    1071            0 :                                 return HCCL_E_INTERNAL;
    1072              :                             }
    1073              :                         }
    1074              :                     } // curReq.isValid
    1075              :                 } // Each TransportRequest
    1076              :             } // Each SingleSubCommTransport
    1077              :         } // Each LevelNSubCommTransport
    1078              :     }
    1079              : 
    1080              :     // 打印debug信息
    1081            0 :     if (UNLIKELY(HcclCheckLogLevel(HCCL_LOG_INFO))) {
    1082            0 :         for (size_t rankId = 0; rankId < userInputMemRanges.size(); ++rankId) {
    1083            0 :             const OpUnfoldMemRange& userInputMemRange = userInputMemRanges[rankId];
    1084            0 :             HCCL_INFO(
    1085              :                 "[AicpuCacheManager][PrepareUserMemRanges] userInputMemRanges[%u] isValid: %d, baseAddr: 0x%016llx, "
    1086              :                 "memSize: %llu, endAddr: 0x%016llx",
    1087              :                 rankId, userInputMemRange.isValid, userInputMemRange.baseAddr, userInputMemRange.memSize,
    1088              :                 userInputMemRange.baseAddr + userInputMemRange.memSize);
    1089              : 
    1090            0 :             const OpUnfoldMemRange& userOutputMemRange = userOutputMemRanges[rankId];
    1091            0 :             HCCL_INFO(
    1092              :                 "[AicpuCacheManager][PrepareUserMemRanges] userOutputMemRanges[%u] isValid: %d, baseAddr: 0x%016llx, "
    1093              :                 "memSize: %llu, endAddr: 0x%016llx",
    1094              :                 rankId, userOutputMemRange.isValid, userOutputMemRange.baseAddr, userOutputMemRange.memSize,
    1095              :                 userOutputMemRange.baseAddr + userOutputMemRange.memSize);
    1096              :         }
    1097              :     }
    1098              : 
    1099            0 :     return HCCL_SUCCESS;
    1100              : }
    1101              : 
    1102            0 : HcclResult AicpuCacheManager::ParseOpParamForCache(
    1103              :     const OpParam& param, HcclDataType& sendType, HcclDataType& recvType, uint64_t& inputSize, uint64_t& outputSize,
    1104              :     const HcclTopoInfo& topoinfo)
    1105              : {
    1106              :     // 注意: 由于ParseOpParamForCache前已经做过NeedOpUnfoldCache检查, 这里不再做重复检验
    1107              : 
    1108            0 :     const HcclCMDType opType = param.opType;
    1109            0 :     const uint32_t rankSize = topoinfo.userRankSize;
    1110              : 
    1111              :     // 准备data type和count
    1112              :     // NOTE: 非V类算子 (DataRes), V类算子 (VDataDes), All2All类算子 (All2AllDataDes), batch类算子
    1113              :     // (BatchSendRecvDataDes/BatchWriteDataDes)
    1114            0 :     if (opType == HcclCMDType::HCCL_CMD_ALLTOALL) { // alltoall算子
    1115              :         // 注意: sendType和recvType一定相同
    1116            0 :         sendType = param.All2AllDataDes.sendType;
    1117            0 :         recvType = param.All2AllDataDes.recvType;
    1118              : 
    1119              :         // 注意: 对于alltoall算子, inputSize和outputSize一定相同 (但不能直接使用param.input/outputSize,
    1120              :         // alltoall算子不会设置这两个字段)
    1121            0 :         inputSize = param.All2AllDataDes.sendCount * rankSize * SIZE_TABLE[sendType];
    1122            0 :         outputSize = inputSize; // 注意: 不能使用param.All2AllDataDes.recvCount * rankSize * SIZE_TABLE[recvType],
    1123              :                                 // 因为alltoall使用sendCount来表示send/recvCount, 而recvCount本身为0
    1124            0 :     } else if (IsAlltoallvType(opType)) { // alltoallv类算子
    1125              :         // 计算相应字段 (虽然GetOpUnfoldKey不需要, 但是PrepareUserMemRanges需要)
    1126              : 
    1127              :         // 注意: sendType和recvType一定相同
    1128            0 :         sendType = param.All2AllDataDes.sendType;
    1129            0 :         recvType = param.All2AllDataDes.recvType;
    1130              : 
    1131              :         // 注意: 对于alltoallv算子, inputSize和outputSize不一定相同 (但不能直接使用param.input/outputSize,
    1132              :         // alltoallv算子不会设置这两个字段)
    1133              :         // 参考coll_all_to_all_v_direct_fullmesh_executor.cc下的CollRunAlltoAllDirectFullmesh::GetLocalSendRecvInfoforAlltoallV
    1134            0 :         inputSize = 0;
    1135            0 :         outputSize = 0;
    1136            0 :         if (opType == HcclCMDType::HCCL_CMD_ALLTOALLV) { // alltoallv算子
    1137            0 :             HCCL_INFO(
    1138              :                 "[AicpuCacheManager][ParseOpParamForCache] sum %u send/recv counts for input/output size", rankSize);
    1139            0 :             for (uint32_t tmpRank = 0; tmpRank < rankSize; ++tmpRank) {
    1140              :                 // curRank发送到tmpRank的数据量
    1141            0 :                 const uint64_t curSendCounts = *(static_cast<const u64*>(param.All2AllDataDes.sendCounts) + tmpRank);
    1142            0 :                 const uint64_t curSendLength = curSendCounts * SIZE_TABLE[sendType];
    1143            0 :                 inputSize += curSendLength;
    1144              : 
    1145              :                 // curRank从tmpRank接收的数据量
    1146            0 :                 const uint64_t curRecvCounts = *(static_cast<const u64*>(param.All2AllDataDes.recvCounts) + tmpRank);
    1147            0 :                 const uint64_t curRecvLength = curRecvCounts * SIZE_TABLE[recvType];
    1148            0 :                 outputSize += curRecvLength;
    1149              :             }
    1150            0 :         } else if (opType == HcclCMDType::HCCL_CMD_ALLTOALLVC) { // alltoallvc算子
    1151            0 :             const uint32_t curRank = topoinfo.userRank;
    1152            0 :             HCCL_INFO(
    1153              :                 "[AicpuCacheManager][ParseOpParamForCache] sum %u-size sendCountMatrix for input/output size",
    1154              :                 rankSize);
    1155            0 :             for (uint32_t tmpRank = 0; tmpRank < rankSize; ++tmpRank) {
    1156              :                 // curRank发送到tmpRank的数据量
    1157            0 :                 const uint64_t curSendCounts
    1158            0 :                     = *(static_cast<const u64*>(param.All2AllDataDes.sendCountMatrix) + curRank * rankSize
    1159            0 :                         + tmpRank); // sendCountMatrix[curRank][tmpRank]
    1160            0 :                 const uint64_t curSendLength = curSendCounts * SIZE_TABLE[sendType];
    1161            0 :                 inputSize += curSendLength;
    1162              : 
    1163              :                 // curRank从tmpRank接收到的数据量
    1164            0 :                 const uint64_t curRecvCounts
    1165            0 :                     = *(static_cast<const u64*>(param.All2AllDataDes.sendCountMatrix) + tmpRank * topoinfo.userRankSize
    1166            0 :                         + curRank); // sendCountMatrix[tmpRank][curRank]
    1167            0 :                 const uint64_t curRecvLength = curRecvCounts * SIZE_TABLE[recvType];
    1168            0 :                 outputSize += curRecvLength;
    1169              :             }
    1170              :         } else {
    1171            0 :             HCCL_ERROR("[AicpuCacheManager][ParseOpParamForCache] invalid opType[%u] for alltoallv", opType);
    1172            0 :             return HCCL_E_INTERNAL;
    1173              :         }
    1174              :     } else { // 非V类算子
    1175            0 :         sendType = param.DataDes.dataType;
    1176            0 :         recvType = param.DataDes.dataType;
    1177            0 :         inputSize = param.inputSize;
    1178            0 :         outputSize = param.outputSize;
    1179              :     }
    1180              : 
    1181            0 :     HCCL_DEBUG(
    1182              :         "[AicpuCacheManager][ParseOpParamForCache] opType[%u] rankSize[%u] sendType[%u] recvType[%u] inputSize[%u] "
    1183              :         "outputSize[%u]",
    1184              :         opType, rankSize, sendType, recvType, inputSize, outputSize);
    1185              : 
    1186            0 :     return HCCL_SUCCESS;
    1187              : }
    1188              : 
    1189            0 : HcclResult AicpuCacheManager::PrepareAlltoallvSendRecvInfo(
    1190              :     const OpParam& param, AlltoallvSendRecvInfo& alltoallvSendRecvInfo, const HcclTopoInfo& topoinfo)
    1191              : {
    1192            0 :     const uint32_t rankSize = topoinfo.userRankSize;
    1193            0 :     HCCL_INFO("[AicpuCacheManager][PrepareAlltoallvSendRecvInfo] prepare %u send/recv info", rankSize);
    1194              : 
    1195              :     // 准备send/recv data type
    1196            0 :     const HcclDataType sendType = param.All2AllDataDes.sendType;
    1197            0 :     alltoallvSendRecvInfo.sendType = sendType;
    1198            0 :     const HcclDataType recvType = param.All2AllDataDes.recvType;
    1199            0 :     alltoallvSendRecvInfo.recvType = recvType;
    1200              : 
    1201              :     // 初始化send/recv counts
    1202            0 :     alltoallvSendRecvInfo.sendCounts.resize(rankSize);
    1203            0 :     alltoallvSendRecvInfo.recvCounts.resize(rankSize);
    1204              : 
    1205              :     // 初始化send/recv offsets
    1206            0 :     alltoallvSendRecvInfo.sendOffsets.resize(rankSize);
    1207            0 :     alltoallvSendRecvInfo.recvOffsets.resize(rankSize);
    1208              : 
    1209              :     // 参考coll_all_to_all_v_direct_fullmesh_executor.cc中的GetLocalSendRecvInfoforAlltoallV
    1210            0 :     const uint32_t sendTypeSize = SIZE_TABLE[sendType];    // Size of sendType in units of bytes
    1211            0 :     const uint32_t recvTypeSize = SIZE_TABLE[recvType];    // Size of recvType in units of bytes
    1212            0 :     if (param.opType == HcclCMDType::HCCL_CMD_ALLTOALLV) { // alltoallv
    1213              :         // 准备send counts
    1214            0 :         for (size_t dstRank = 0; dstRank < rankSize; ++dstRank) {
    1215            0 :             alltoallvSendRecvInfo.sendCounts[dstRank]
    1216            0 :                 = *(static_cast<const u64*>(param.All2AllDataDes.sendCounts) + dstRank);
    1217              :         }
    1218              : 
    1219              :         // 准备recv counts
    1220            0 :         for (size_t dstRank = 0; dstRank < rankSize; ++dstRank) {
    1221            0 :             alltoallvSendRecvInfo.recvCounts[dstRank]
    1222            0 :                 = *(static_cast<const u64*>(param.All2AllDataDes.recvCounts) + dstRank);
    1223              :         }
    1224              : 
    1225              :         // 准备send offsets
    1226            0 :         for (size_t dstRank = 0; dstRank < rankSize; ++dstRank) {
    1227            0 :             const uint64_t curSendDispls = *(static_cast<const uint64_t*>(param.All2AllDataDes.sdispls) + dstRank);
    1228            0 :             alltoallvSendRecvInfo.sendOffsets[dstRank] = curSendDispls * sendTypeSize;
    1229              :         }
    1230              : 
    1231              :         // 准备recv offsets
    1232            0 :         for (size_t dstRank = 0; dstRank < rankSize; ++dstRank) {
    1233            0 :             const uint64_t curRecvDispls = *(static_cast<const uint64_t*>(param.All2AllDataDes.rdispls) + dstRank);
    1234            0 :             alltoallvSendRecvInfo.recvOffsets[dstRank] = curRecvDispls * recvTypeSize;
    1235              :         }
    1236            0 :     } else if (param.opType == HcclCMDType::HCCL_CMD_ALLTOALLVC) { // alltoallvc
    1237            0 :         const uint32_t curRank = topoinfo.userRank;
    1238              : 
    1239              :         // 准备send counts
    1240            0 :         for (size_t tmpRank = 0; tmpRank < rankSize; ++tmpRank) {
    1241            0 :             alltoallvSendRecvInfo.sendCounts[tmpRank]
    1242            0 :                 = *(static_cast<const u64*>(param.All2AllDataDes.sendCountMatrix) + curRank * rankSize
    1243            0 :                     + tmpRank); // sendCountMatrix[curRank][tmpRank]
    1244              :         }
    1245              : 
    1246              :         // 准备recv counts
    1247            0 :         for (size_t tmpRank = 0; tmpRank < rankSize; ++tmpRank) {
    1248            0 :             alltoallvSendRecvInfo.recvCounts[tmpRank]
    1249            0 :                 = *(static_cast<const u64*>(param.All2AllDataDes.sendCountMatrix) + tmpRank * topoinfo.userRankSize
    1250            0 :                     + curRank); // sendCountMatrix[tmpRank][curRank]
    1251              :         }
    1252              : 
    1253              :         // 准备send offsets
    1254            0 :         uint64_t curSendDispls = 0;
    1255            0 :         for (size_t tmpRank = 0; tmpRank < rankSize; ++tmpRank) {
    1256            0 :             alltoallvSendRecvInfo.sendOffsets[tmpRank] = curSendDispls * sendTypeSize;
    1257            0 :             curSendDispls += alltoallvSendRecvInfo.sendCounts[tmpRank];
    1258              :         }
    1259              : 
    1260              :         // 准备recv offsets
    1261            0 :         uint64_t curRecvDispls = 0;
    1262            0 :         for (size_t tmpRank = 0; tmpRank < rankSize; ++tmpRank) {
    1263            0 :             alltoallvSendRecvInfo.recvOffsets[tmpRank] = curRecvDispls * recvTypeSize;
    1264            0 :             curRecvDispls += alltoallvSendRecvInfo.recvCounts[tmpRank];
    1265              :         }
    1266              :     } else {
    1267            0 :         HCCL_ERROR("[AicpuCacheManager][PrepareAlltoallvSendRecvInfo] invalid opType[%u] for alltoallv", param.opType);
    1268            0 :         return HCCL_E_INTERNAL;
    1269              :     }
    1270              : 
    1271            0 :     return HCCL_SUCCESS;
    1272              : }
    1273              : 
    1274              : } // namespace hccl
        

Generated by: LCOV version 2.0-1