已合并
将 BIRS 批量不变性扩展至变长 ReduceScatterV(ReduceScatterVBIRS / ReduceScatterVBIRSInter) #2338
Davydov_Danil创建于 7月28日
将 BIRS 批量不变性扩展至变长 ReduceScatterV(ReduceScatterVBIRS / ReduceScatterVBIRSInter) #2338
已合并
共 35 个文件变更+2799-7
| @@ -531,6 +531,7 @@ function run_st() { | |||
| 531 | log "Info: run_st ST_TASKS=${ST_TASKS}" | 531 | log "Info: run_st ST_TASKS=${ST_TASKS}" |
| 532 | export ENABLE_GCOV=${ENABLE_GCOV} | 532 | export ENABLE_GCOV=${ENABLE_GCOV} |
| 533 | export ST_TASKS=${ST_TASKS} | 533 | export ST_TASKS=${ST_TASKS} |
| 534 | + export ENABLE_EXPERIMENTAL=${ENABLE_EXPERIMENTAL} | ||
| 534 | # 编译 ST 用例 | 535 | # 编译 ST 用例 |
| 535 | bash ${st_build_shell} | 536 | bash ${st_build_shell} |
| 536 | local build_ret=$? | 537 | local build_ret=$? |
| @@ -14,6 +14,7 @@ endif() | |||
| 14 | 14 | ||
| 15 | add_subdirectory(op_common) | 15 | add_subdirectory(op_common) |
| 16 | add_subdirectory(reduce_scatter) | 16 | add_subdirectory(reduce_scatter) |
| 17 | +add_subdirectory(reduce_scatter_v) | ||
| 17 | add_subdirectory(all_reduce/example) | 18 | add_subdirectory(all_reduce/example) |
| 18 | 19 | ||
| 19 | set(EXPERIMENTAL_INCLUDE_LIST | 20 | set(EXPERIMENTAL_INCLUDE_LIST |
| @@ -25,6 +26,9 @@ set(EXPERIMENTAL_INCLUDE_LIST | |||
| 25 | ${CMAKE_CURRENT_SOURCE_DIR}/reduce_scatter/ | 26 | ${CMAKE_CURRENT_SOURCE_DIR}/reduce_scatter/ |
| 26 | ${CMAKE_CURRENT_SOURCE_DIR}/reduce_scatter/birs/ | 27 | ${CMAKE_CURRENT_SOURCE_DIR}/reduce_scatter/birs/ |
| 27 | ${CMAKE_CURRENT_SOURCE_DIR}/reduce_scatter/birs/template/ | 28 | ${CMAKE_CURRENT_SOURCE_DIR}/reduce_scatter/birs/template/ |
| 29 | + ${CMAKE_CURRENT_SOURCE_DIR}/reduce_scatter_v/ | ||
| 30 | + ${CMAKE_CURRENT_SOURCE_DIR}/reduce_scatter_v/birs/ | ||
| 31 | + ${CMAKE_CURRENT_SOURCE_DIR}/reduce_scatter_v/birs/template/ | ||
| 28 | ${CMAKE_CURRENT_SOURCE_DIR}/all_reduce/ | 32 | ${CMAKE_CURRENT_SOURCE_DIR}/all_reduce/ |
| 29 | ${CMAKE_CURRENT_SOURCE_DIR}/all_reduce/example/ | 33 | ${CMAKE_CURRENT_SOURCE_DIR}/all_reduce/example/ |
| 30 | ${CMAKE_CURRENT_SOURCE_DIR}/all_reduce/example/template/ | 34 | ${CMAKE_CURRENT_SOURCE_DIR}/all_reduce/example/template/ |
| @@ -43,6 +47,10 @@ if(TARGET scatter_aicpu_kernel) | |||
| 43 | ${CMAKE_CURRENT_SOURCE_DIR}/reduce_scatter/birs/reduce_scatter_executor_base.cc | 47 | ${CMAKE_CURRENT_SOURCE_DIR}/reduce_scatter/birs/reduce_scatter_executor_base.cc |
| 44 | ${CMAKE_CURRENT_SOURCE_DIR}/reduce_scatter/birs/template/reduce_scatter_birs.cc | 48 | ${CMAKE_CURRENT_SOURCE_DIR}/reduce_scatter/birs/template/reduce_scatter_birs.cc |
| 45 | ${CMAKE_CURRENT_SOURCE_DIR}/reduce_scatter/birs/template/reduce_scatter_birs_inter.cc | 49 | ${CMAKE_CURRENT_SOURCE_DIR}/reduce_scatter/birs/template/reduce_scatter_birs_inter.cc |
| 50 | + ${CMAKE_CURRENT_SOURCE_DIR}/reduce_scatter_v/birs/reduce_scatter_v_birs_executor.cc | ||
| 51 | + ${CMAKE_CURRENT_SOURCE_DIR}/reduce_scatter_v/birs/reduce_scatter_v_executor_base.cc | ||
| 52 | + ${CMAKE_CURRENT_SOURCE_DIR}/reduce_scatter_v/birs/template/reduce_scatter_v_birs.cc | ||
| 53 | + ${CMAKE_CURRENT_SOURCE_DIR}/reduce_scatter_v/birs/template/reduce_scatter_v_birs_inter.cc | ||
| 46 | ) | 54 | ) |
| 47 | endif() | 55 | endif() |
| 48 | # host 构建存在 hccl 目标; device 构建无 hccl 目标(仅 scatter_aicpu_kernel),此处需守护以兼容 device 复用本目录 | 56 | # host 构建存在 hccl 目标; device 构建无 hccl 目标(仅 scatter_aicpu_kernel),此处需守护以兼容 device 复用本目录 |
| @@ -19,6 +19,7 @@ | |||
| 19 | #include "topo_host.h" | 19 | #include "topo_host.h" |
| 20 | #include "hcomm_host_profiling_dl.h" | 20 | #include "hcomm_host_profiling_dl.h" |
| 21 | #include "reduce_scatter_birs_selector.h" | 21 | #include "reduce_scatter_birs_selector.h" |
| 22 | +#include "reduce_scatter_v_birs_selector.h" | ||
| 22 | #include <algorithm> | 23 | #include <algorithm> |
| 23 | #include <future> | 24 | #include <future> |
| 24 | #include <map> | 25 | #include <map> |
| @@ -31,6 +32,7 @@ using ops_hccl::AlgType; | |||
| 31 | using ops_hccl::AlgTypeLevel1; | 32 | using ops_hccl::AlgTypeLevel1; |
| 32 | using ops_hccl::CollAlgExecRegistry; | 33 | using ops_hccl::CollAlgExecRegistry; |
| 33 | using ops_hccl::CUSTOM_TIMEOUT; | 34 | using ops_hccl::CUSTOM_TIMEOUT; |
| 35 | +using ops_hccl::DATATYPE_SIZE_TABLE; | ||
| 34 | using ops_hccl::ExecutorBase; | 36 | using ops_hccl::ExecutorBase; |
| 35 | using ops_hccl::g_binKernelHandle; | 37 | using ops_hccl::g_binKernelHandle; |
| 36 | using ops_hccl::g_notifiesMap; | 38 | using ops_hccl::g_notifiesMap; |
| @@ -41,7 +43,9 @@ using ops_hccl::haclrtMemcpy; | |||
| 41 | using ops_hccl::HCCL_ALG; | 43 | using ops_hccl::HCCL_ALG; |
| 42 | using ops_hccl::NotifyArray; | 44 | using ops_hccl::NotifyArray; |
| 43 | using ops_hccl::OpParam; | 45 | using ops_hccl::OpParam; |
| 46 | +using ops_hccl::REDUCE_SCATTER_V_VECTOR_NUM; | ||
| 44 | using ops_hccl::TopoInfo; | 47 | using ops_hccl::TopoInfo; |
| 48 | + | ||
| 45 | bool IsStreamCapture(aclrtStream stream) | 49 | bool IsStreamCapture(aclrtStream stream) |
| 46 | { | 50 | { |
| 47 | bool isCapture; | 51 | bool isCapture; |
| @@ -148,13 +152,70 @@ SelectAlgReduceScatter(HcclComm comm, OpParam& param, TopoInfo* topoInfo, AlgTyp | |||
| 148 | return HCCL_SUCCESS; | 152 | return HCCL_SUCCESS; |
| 149 | } | 153 | } |
| 150 | 154 | ||
| 155 | +HcclResult | ||
| 156 | +SelectAlgReduceScatterV(HcclComm comm, OpParam& param, TopoInfo* topoInfo, AlgType& algType, std::string& algName) | ||
| 157 | +{ | ||
| 158 | + (void)comm; | ||
| 159 | + ValidateAndResetAlgLevel1(algType, "Reduce_Scatter_V"); | ||
| 160 | + | ||
| 161 | + const BirsVSelectResult birsVResult = DecideReduceScatterVBirsAlg(*topoInfo, algName); | ||
| 162 | + const HcclResult birsVRet = BirsVSelectResultToCode(birsVResult); | ||
| 163 | + if (birsVRet != HCCL_SUCCESS) { | ||
| 164 | + if (birsVResult == BirsVSelectResult::vRejectServerNumZero) { | ||
| 165 | + HCCL_ERROR( | ||
| 166 | + "[%s] ReduceScatterVBIRS not supported: serverNum is 0, cannot compute ranks per server " | ||
| 167 | + "(userRankSize[%u])", | ||
| 168 | + __func__, topoInfo->userRankSize); | ||
| 169 | + } else if (birsVResult == BirsVSelectResult::vRejectRanksPerServerLT4) { | ||
| 170 | + HCCL_ERROR( | ||
| 171 | + "[%s] ReduceScatterVBIRS not supported: userRankSize[%u] / serverNum[%u] < 4", __func__, | ||
| 172 | + topoInfo->userRankSize, topoInfo->serverNum); | ||
| 173 | + } | ||
| 174 | + return birsVRet; | ||
| 175 | + } | ||
| 176 | + | ||
| 177 | + CHK_RET(FillAlgTagAndDebugInfo(param, topoInfo, algType, algName, "Reduce_Scatter_V")); | ||
| 178 | + return HCCL_SUCCESS; | ||
| 179 | +} | ||
| 180 | + | ||
| 151 | struct ThreadResources { | 181 | struct ThreadResources { |
| 152 | ThreadHandle cpuTsThread = 0; | 182 | ThreadHandle cpuTsThread = 0; |
| 153 | ThreadHandle exportedAicpuTsThread = 0; | 183 | ThreadHandle exportedAicpuTsThread = 0; |
| 154 | ThreadHandle exportedCpuTsThread = 0; | 184 | ThreadHandle exportedCpuTsThread = 0; |
| 155 | AlgResourceCtx* resCtx = nullptr; | 185 | AlgResourceCtx* resCtx = nullptr; |
| 186 | + void* vDataDesDevPtr = nullptr; | ||
| 156 | }; | 187 | }; |
| 157 | 188 | ||
| 189 | +HcclResult PrepareReduceScatterVVarData(OpParam& param, TopoInfo* topoInfo, ThreadResources& threadRes) | ||
| 190 | +{ | ||
| 191 | + if (param.engine != COMM_ENGINE_AICPU_TS || param.opType != HCCL_CMD_REDUCE_SCATTER_V || param.varMemSize <= 0) { | ||
| 192 | + return HCCL_SUCCESS; | ||
| 193 | + } | ||
| 194 | + u32 userRankSize = topoInfo->userRankSize; | ||
| 195 | + CHK_PRT_RET( | ||
| 196 | + param.varMemSize != REDUCE_SCATTER_V_VECTOR_NUM * userRankSize * sizeof(u64), | ||
| 197 | + HCCL_ERROR( | ||
| 198 | + "[PrepareReduceScatterVVarData] param.varMemSize[%llu] invalid, expected[%llu]", param.varMemSize, | ||
| 199 | + REDUCE_SCATTER_V_VECTOR_NUM * userRankSize * sizeof(u64)), | ||
| 200 | + HCCL_E_PARA); | ||
| 201 | + ACLCHECK(aclrtMalloc(&threadRes.vDataDesDevPtr, param.varMemSize, ACL_MEM_MALLOC_HUGE_FIRST)); | ||
| 202 | + u64* devCounts = static_cast<u64*>(threadRes.vDataDesDevPtr); | ||
| 203 | + aclError aclRet | ||
| 204 | + = aclrtMemcpy(devCounts, param.varMemSize, param.varData, param.varMemSize, ACL_MEMCPY_HOST_TO_DEVICE); | ||
| 205 | + if (aclRet != ACL_SUCCESS) { | ||
| 206 | + HCCL_ERROR("acl interface return err %s:%d, retcode: %d.", __FILE__, __LINE__, aclRet); | ||
| 207 | + aclError freeRet = aclrtFree(threadRes.vDataDesDevPtr); | ||
| 208 | + if (freeRet != ACL_SUCCESS) { | ||
| 209 | + HCCL_WARNING("[PrepareReduceScatterVVarData] aclrtFree vDataDesDevPtr failed, ret[%d]", freeRet); | ||
| 210 | + } | ||
| 211 | + threadRes.vDataDesDevPtr = nullptr; | ||
| 212 | + return HCCL_E_RUNTIME; | ||
| 213 | + } | ||
| 214 | + param.vDataDes.counts = devCounts; | ||
| 215 | + param.vDataDes.displs = devCounts + userRankSize; | ||
| 216 | + return HCCL_SUCCESS; | ||
| 217 | +} | ||
| 218 | + | ||
| 158 | HcclResult PrepareThreadResources( | 219 | HcclResult PrepareThreadResources( |
| 159 | HcclComm comm, OpParam& param, std::unique_ptr<ExecutorBase>& executor, TopoInfo* topoInfo, AlgType& algType, | 220 | HcclComm comm, OpParam& param, std::unique_ptr<ExecutorBase>& executor, TopoInfo* topoInfo, AlgType& algType, |
| 160 | ThreadResources& threadRes) | 221 | ThreadResources& threadRes) |
| @@ -190,6 +251,9 @@ HcclResult PrepareThreadResources( | |||
| 190 | curPtr, sizeof(ThreadHandle), &threadRes.exportedAicpuTsThread, sizeof(ThreadHandle), | 251 | curPtr, sizeof(ThreadHandle), &threadRes.exportedAicpuTsThread, sizeof(ThreadHandle), |
| 191 | ACL_MEMCPY_HOST_TO_DEVICE)); | 252 | ACL_MEMCPY_HOST_TO_DEVICE)); |
| 192 | } | 253 | } |
| 254 | + | ||
| 255 | + CHK_RET(PrepareReduceScatterVVarData(param, topoInfo, threadRes)); | ||
| 256 | + | ||
| 193 | return HCCL_SUCCESS; | 257 | return HCCL_SUCCESS; |
| 194 | } | 258 | } |
| 195 | 259 | ||
| @@ -322,6 +386,8 @@ HcclResult ExecOpBirs(HcclComm comm, OpParam& param) | |||
| 322 | std::string algName; | 386 | std::string algName; |
| 323 | if (param.opType == HCCL_CMD_REDUCE_SCATTER) { | 387 | if (param.opType == HCCL_CMD_REDUCE_SCATTER) { |
| 324 | CHK_RET(SelectAlgReduceScatter(comm, param, topoInfo, algType, algName)); | 388 | CHK_RET(SelectAlgReduceScatter(comm, param, topoInfo, algType, algName)); |
| 389 | + } else if (param.opType == HCCL_CMD_REDUCE_SCATTER_V) { | ||
| 390 | + CHK_RET(SelectAlgReduceScatterV(comm, param, topoInfo, algType, algName)); | ||
| 325 | } | 391 | } |
| 326 | 392 | ||
| 327 | std::unique_ptr<ExecutorBase> executor = CollAlgExecRegistry::Instance().GetAlgExec(algName); | 393 | std::unique_ptr<ExecutorBase> executor = CollAlgExecRegistry::Instance().GetAlgExec(algName); |
| @@ -333,7 +399,16 @@ HcclResult ExecOpBirs(HcclComm comm, OpParam& param) | |||
| 333 | CHK_RET(PrepareThreadResources(comm, param, executor, topoInfo, algType, threadRes)); | 399 | CHK_RET(PrepareThreadResources(comm, param, executor, topoInfo, algType, threadRes)); |
| 334 | 400 | ||
| 335 | if (param.engine == COMM_ENGINE_AICPU_TS) { | 401 | if (param.engine == COMM_ENGINE_AICPU_TS) { |
| 336 | - CHK_RET(LaunchAicpuKernel(comm, param, topoInfo, algName, threadRes)); | 402 | + // 先保存返回值再释放 vDataDesDevPtr, 保证 LaunchAicpuKernel 失败路径同样释放设备内存 |
| 403 | + HcclResult launchRet = LaunchAicpuKernel(comm, param, topoInfo, algName, threadRes); | ||
| 404 | + if (threadRes.vDataDesDevPtr != nullptr) { | ||
| 405 | + aclError aclRet = aclrtFree(threadRes.vDataDesDevPtr); | ||
| 406 | + if (aclRet != ACL_SUCCESS) { | ||
| 407 | + HCCL_WARNING("[ExecOpBirs] aclrtFree vDataDesDevPtr failed, ret[%d]", aclRet); | ||
| 408 | + } | ||
| 409 | + threadRes.vDataDesDevPtr = nullptr; | ||
| 410 | + } | ||
| 411 | + CHK_RET(launchRet); | ||
| 337 | } else { | 412 | } else { |
| 338 | CHK_RET(executor->Orchestrate(param, threadRes.resCtx)); | 413 | CHK_RET(executor->Orchestrate(param, threadRes.resCtx)); |
| 339 | param.resCtx = threadRes.resCtx; | 414 | param.resCtx = threadRes.resCtx; |
| @@ -351,8 +426,13 @@ HcclResult ProcessA3(HcclComm comm, OpParam& param, uint64_t beginTime) | |||
| 351 | CHK_SAFETY_FUNC_RET(strcpy_s(profInfo.algType, sizeof(profInfo.algType), algTypeStr.c_str())); | 426 | CHK_SAFETY_FUNC_RET(strcpy_s(profInfo.algType, sizeof(profInfo.algType), algTypeStr.c_str())); |
| 352 | CHK_SAFETY_FUNC_RET(strcpy_s(profInfo.commName, sizeof(profInfo.commName), param.commName)); | 427 | CHK_SAFETY_FUNC_RET(strcpy_s(profInfo.commName, sizeof(profInfo.commName), param.commName)); |
| 353 | profInfo.beginTime = beginTime; | 428 | profInfo.beginTime = beginTime; |
| 354 | - profInfo.dataCount = param.DataDes.count; | 429 | + if (param.opType == HCCL_CMD_REDUCE_SCATTER_V) { |
| 355 | - profInfo.dataType = static_cast<uint8_t>(param.DataDes.dataType); | 430 | + profInfo.dataCount = param.outputSize / DATATYPE_SIZE_TABLE[param.vDataDes.dataType]; |
| 431 | + profInfo.dataType = static_cast<uint8_t>(param.vDataDes.dataType); | ||
| 432 | + } else { | ||
| 433 | + profInfo.dataCount = param.DataDes.count; | ||
| 434 | + profInfo.dataType = static_cast<uint8_t>(param.DataDes.dataType); | ||
| 435 | + } | ||
| 356 | profInfo.cmdType = static_cast<uint8_t>(param.opType); | 436 | profInfo.cmdType = static_cast<uint8_t>(param.opType); |
| 357 | CHK_PRT(HcommProfilingReportOp(profInfo)); | 437 | CHK_PRT(HcommProfilingReportOp(profInfo)); |
| 358 | 438 | ||
| @@ -24,6 +24,8 @@ using ops_hccl::TemplateType; | |||
| 24 | 24 | ||
| 25 | constexpr TemplateType TEMPLATE_REDUCE_SCATTER_BIRS = static_cast<TemplateType>(1001); | 25 | constexpr TemplateType TEMPLATE_REDUCE_SCATTER_BIRS = static_cast<TemplateType>(1001); |
| 26 | constexpr TemplateType TEMPLATE_REDUCE_SCATTER_BIRS_INTER = static_cast<TemplateType>(1002); | 26 | constexpr TemplateType TEMPLATE_REDUCE_SCATTER_BIRS_INTER = static_cast<TemplateType>(1002); |
| 27 | +constexpr TemplateType TEMPLATE_REDUCE_SCATTER_V_BIRS = static_cast<TemplateType>(1003); | ||
| 28 | +constexpr TemplateType TEMPLATE_REDUCE_SCATTER_V_BIRS_INTER = static_cast<TemplateType>(1004); | ||
| 27 | 29 | ||
| 28 | class AlgTemplateBaseExperimental : public ops_hccl::AlgTemplateBase { | 30 | class AlgTemplateBaseExperimental : public ops_hccl::AlgTemplateBase { |
| 29 | public: | 31 | public: |
| @@ -0,0 +1,20 @@ | |||
| 1 | +# ----------------------------------------------------------------------------------------------------------- | ||
| 2 | +# Copyright (c) 2026 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 | +add_subdirectory(birs) | ||
| 11 | + | ||
| 12 | +set(src_list | ||
| 13 | + ${CMAKE_CURRENT_SOURCE_DIR}/reduce_scatter_v_op_experimental.cc | ||
| 14 | +) | ||
| 15 | + | ||
| 16 | +if(TARGET hccl) | ||
| 17 | + target_sources(hccl PRIVATE | ||
| 18 | + ${src_list} | ||
| 19 | + ) | ||
| 20 | +endif() | ||
| @@ -0,0 +1,22 @@ | |||
| 1 | +# ----------------------------------------------------------------------------------------------------------- | ||
| 2 | +# Copyright (c) 2026 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 | +add_subdirectory(template) | ||
| 11 | + | ||
| 12 | +set(src_list | ||
| 13 | + ${CMAKE_CURRENT_SOURCE_DIR}/reduce_scatter_v_executor_base.cc | ||
| 14 | + ${CMAKE_CURRENT_SOURCE_DIR}/reduce_scatter_v_birs_executor.cc | ||
| 15 | + ${CMAKE_CURRENT_SOURCE_DIR}/reduce_scatter_v_birs_selector.cc | ||
| 16 | +) | ||
| 17 | + | ||
| 18 | +if(TARGET hccl) | ||
| 19 | + target_sources(hccl PRIVATE | ||
| 20 | + ${src_list} | ||
| 21 | + ) | ||
| 22 | +endif() | ||
| @@ -0,0 +1,122 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 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 <numeric> | ||
| 12 | +#include "reduce_scatter_v_birs_executor.h" | ||
| 13 | +#include "topo_experimental.h" | ||
| 14 | + | ||
| 15 | +namespace ops_hccl_experimental { | ||
| 16 | +ReduceScatterVBIRSExecutor::ReduceScatterVBIRSExecutor() : ReduceScatterVExecutorBase() | ||
| 17 | +{ | ||
| 18 | + desc_.level1SupportedAlgos | ||
| 19 | + = {AlgTypeLevel1::ALG_LEVEL1_NHR, AlgTypeLevel1::ALG_LEVEL1_NB, AlgTypeLevel1::ALG_LEVEL1_RING}; | ||
| 20 | + desc_.level2SupportedAlgos | ||
| 21 | + = {AlgTypeLevel2::ALG_LEVEL2_NHR, AlgTypeLevel2::ALG_LEVEL2_NB, AlgTypeLevel2::ALG_LEVEL2_RING}; | ||
| 22 | +} | ||
| 23 | + | ||
| 24 | +HcclResult ReduceScatterVBIRSExecutor::CalcResRequest( | ||
| 25 | + HcclComm comm, const OpParam& param, TopoInfo* topoInfo, AlgHierarchyInfo& algHierarchyInfo, | ||
| 26 | + AlgResourceRequest& resourceRequest, AlgType& algType) | ||
| 27 | +{ | ||
| 28 | + if (topoInfo->serverNum == 1) { | ||
| 29 | + CHK_RET(CalcGeneralTopoInfoForA3(comm, topoInfo, algHierarchyInfo)); | ||
| 30 | + } else { | ||
| 31 | + CHK_RET(CalcGeneralTopoInfoInterServer(comm, topoInfo, algHierarchyInfo)); | ||
| 32 | + } | ||
| 33 | + CHK_RET(RefreshAlgType(algType)); | ||
| 34 | + algType.algoLevel0 = AlgTypeLevel0::ALG_LEVEL0_NP_MESH; | ||
| 35 | + | ||
| 36 | + resourceRequest.slaveThreadNum = BIRS_THREAD_NUM; | ||
| 37 | + for (u32 index = 0; index < BIRS_THREAD_NUM; index++) { | ||
| 38 | + resourceRequest.notifyNumPerThread.push_back(1); | ||
| 39 | + } | ||
| 40 | + resourceRequest.notifyNumOnMainThread = BIRS_THREAD_NUM; | ||
| 41 | + | ||
| 42 | + // level0 channel | ||
| 43 | + std::vector<HcclChannelDesc> level0Channels; | ||
| 44 | + CHK_RET(CalcLevel0ChannelRequest(param, topoInfo, algHierarchyInfo, algType, level0Channels)); | ||
| 45 | + resourceRequest.channels.push_back(level0Channels); | ||
| 46 | + | ||
| 47 | + HCCL_INFO( | ||
| 48 | + "[ReduceScatterVBIRSExecutor][CalcResRequest]slaveThreadNum[%u] notifyNumPerThread[%u]" | ||
| 49 | + " notifyNumOnMainThread[%u] level0Channels[%u]", | ||
| 50 | + resourceRequest.slaveThreadNum, resourceRequest.notifyNumPerThread.size(), | ||
| 51 | + resourceRequest.notifyNumOnMainThread, level0Channels.size()); | ||
| 52 | + return HCCL_SUCCESS; | ||
| 53 | +} | ||
| 54 | + | ||
| 55 | +HcclResult ReduceScatterVBIRSExecutor::KernelRun(const OpParam& param, ExecMem& execMem) | ||
| 56 | +{ | ||
| 57 | + HCCL_CONFIG_INFO(HCCL_ALG, "[ReduceScatterVBIRSExecutor][KernelRun] starts."); | ||
| 58 | + | ||
| 59 | + CHK_RET(KernelRunLevel0(param, execMem)); | ||
| 60 | + | ||
| 61 | + HCCL_INFO("reduce scatter V BIRS run success"); | ||
| 62 | + return HCCL_SUCCESS; | ||
| 63 | +} | ||
| 64 | + | ||
| 65 | +HcclResult ReduceScatterVBIRSExecutor::SelectAndPrepareBirsVTemplate( | ||
| 66 | + bool isSingleServer, u32 localRank, u32 localRankSize, std::unique_ptr<AlgTemplateBase>& templatePtr) | ||
| 67 | +{ | ||
| 68 | + TemplateType tplType = isSingleServer ? TEMPLATE_REDUCE_SCATTER_V_BIRS : TEMPLATE_REDUCE_SCATTER_V_BIRS_INTER; | ||
| 69 | + templatePtr = AlgTemplateRegistry::Instance().GetAlgTemplate(tplType); | ||
| 70 | + CHK_SMART_PTR_NULL(templatePtr); | ||
| 71 | + if (isSingleServer) { | ||
| 72 | + CHK_RET(templatePtr->Prepare(localRank, localRankSize)); | ||
| 73 | + HCCL_CONFIG_INFO(HCCL_ALG, "[%s] Run TEMPLATE_REDUCE_SCATTER_V_BIRS in COMM_LEVEL0", __func__); | ||
| 74 | + } else { | ||
| 75 | + CHK_RET(templatePtr->Prepare(topoInfo_->serverNum, topoInfo_->userRankSize)); | ||
| 76 | + HCCL_CONFIG_INFO(HCCL_ALG, "[%s] Run TEMPLATE_REDUCE_SCATTER_V_BIRS_INTER in COMM_LEVEL0", __func__); | ||
| 77 | + } | ||
| 78 | + return HCCL_SUCCESS; | ||
| 79 | +} | ||
| 80 | + | ||
| 81 | +HcclResult ReduceScatterVBIRSExecutor::KernelRunLevel0(const OpParam& param, ExecMem& execMem) | ||
| 82 | +{ | ||
| 83 | + SubCommInfo level0CommInfo; | ||
| 84 | + CHK_RET(GetSubCommInfo(COMM_LEVEL0, level0CommInfo)); | ||
| 85 | + u32 level0LocalRank = level0CommInfo.localRank; | ||
| 86 | + u32 level0LocalRankSize = level0CommInfo.localRankSize; | ||
| 87 | + u32 sliceNum = level0LocalRankSize; | ||
| 88 | + | ||
| 89 | + // V变体: 从param.vDataDes.counts/displs还原各rank的sendCounts与sendDispls | ||
| 90 | + // 注意: 使用vDataDes(常规字段, sizeof(OpParam)内)而非varData(柔性数组), 因为AICPU_TS | ||
| 91 | + // kernel下发只拷贝sizeof(OpParam) | ||
| 92 | + const u64* sendCounts = reinterpret_cast<const u64*>(param.vDataDes.counts); | ||
| 93 | + const u64* sendDispls = reinterpret_cast<const u64*>(param.vDataDes.displs); | ||
| 94 | + | ||
| 95 | + std::vector<Slice> dataSegsSlice; | ||
| 96 | + CHK_RET(PrepareDataSliceV(sendCounts, sendDispls, unitSize_, sliceNum, dataSegsSlice)); | ||
| 97 | + | ||
| 98 | + std::unique_ptr<AlgTemplateBase> level0TempAlg = std::make_unique<AlgTemplateBaseExperimental>(); | ||
| 99 | + bool isSingleServer = (topoInfo_->serverNum == 1); | ||
| 100 | + CHK_RET(SelectAndPrepareBirsVTemplate(isSingleServer, level0LocalRank, level0LocalRankSize, level0TempAlg)); | ||
| 101 | + | ||
| 102 | + // V变体: 用户输入区大小为param.inputSize(各rank按sendDispls布局), 输出区为param.outputSize(本rank接收量) | ||
| 103 | + HcclMem UsrInputMem{HCCL_MEM_TYPE_DEVICE, execMem.inputPtr, param.inputSize}; | ||
| 104 | + HcclMem UsrOutputMem{HCCL_MEM_TYPE_DEVICE, execMem.outputPtr, param.outputSize}; | ||
| 105 | + | ||
| 106 | + if (auto exp = dynamic_cast<AlgTemplateBaseExperimental*>(level0TempAlg.get())) { | ||
| 107 | + // count传本rank接收量(execMem.count), slices传变长切片, 由模板内部按slices_[i].size处理各rank数据量 | ||
| 108 | + CHK_RET(exp->Prepare( | ||
| 109 | + UsrInputMem, UsrOutputMem, execMem.inputMem, execMem.count, param.vDataDes.dataType, thread_, slaveThreads_, | ||
| 110 | + param.reduceType, 0, dataSegsSlice, 0, false)); | ||
| 111 | + | ||
| 112 | + CHK_RET(exp->RunAsync(level0LocalRank, level0LocalRankSize, channels_[COMM_LEVEL0])); | ||
| 113 | + } else { | ||
| 114 | + HCCL_ERROR("[KernelRunLevel0] dynamic_cast to AlgTemplateBaseExperimental failed"); | ||
| 115 | + return HCCL_E_INTERNAL; | ||
| 116 | + } | ||
| 117 | + | ||
| 118 | + return HCCL_SUCCESS; | ||
| 119 | +} | ||
| 120 | + | ||
| 121 | +REGISTER_EXEC("ReduceScatterVBIRSExecutor", ReduceScatterVBIRS, ReduceScatterVBIRSExecutor); | ||
| 122 | +} // namespace ops_hccl_experimental | ||
| @@ -0,0 +1,59 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 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 | +#ifndef REDUCE_SCATTER_V_BIRS_EXECUTOR_H | ||
| 12 | +#define REDUCE_SCATTER_V_BIRS_EXECUTOR_H | ||
| 13 | +#include "reduce_scatter_v_executor_base.h" | ||
| 14 | +#include "alg_template_base_experimental.h" | ||
| 15 | +#include "coll_alg_exec_registry.h" | ||
| 16 | + | ||
| 17 | +namespace ops_hccl_experimental { | ||
| 18 | +using ops_hccl::AddAlgToAllAlgos; | ||
| 19 | +using ops_hccl::AlgHierarchyInfo; | ||
| 20 | +using ops_hccl::AlgResourceCtx; | ||
| 21 | +using ops_hccl::AlgResourceRequest; | ||
| 22 | +using ops_hccl::AlgTemplateBase; | ||
| 23 | +using ops_hccl::AlgTemplateRegistry; | ||
| 24 | +using ops_hccl::AlgType; | ||
| 25 | +using ops_hccl::AlgTypeLevel0; | ||
| 26 | +using ops_hccl::AlgTypeLevel1; | ||
| 27 | +using ops_hccl::AlgTypeLevel2; | ||
| 28 | +using ops_hccl::ChannelInfo; | ||
| 29 | +using ops_hccl::CollAlgExecRegistry; | ||
| 30 | +using ops_hccl::COMM_LEVEL0; | ||
| 31 | +using ops_hccl::DefaultExecCreator; | ||
| 32 | +using ops_hccl::ExecMem; | ||
| 33 | +using ops_hccl::ExecutorBase; | ||
| 34 | +using ops_hccl::HCCL_ALG; | ||
| 35 | +using ops_hccl::OpParam; | ||
| 36 | +using ops_hccl::Slice; | ||
| 37 | +using ops_hccl::SubCommInfo; | ||
| 38 | +using ops_hccl::TemplateType; | ||
| 39 | +using ops_hccl::TopoInfo; | ||
| 40 | + | ||
| 41 | +class ReduceScatterVBIRSExecutor : public ReduceScatterVExecutorBase { | ||
| 42 | +public: | ||
| 43 | + explicit ReduceScatterVBIRSExecutor(); | ||
| 44 | + ~ReduceScatterVBIRSExecutor() override = default; | ||
| 45 | + | ||
| 46 | + HcclResult CalcResRequest( | ||
| 47 | + HcclComm comm, const OpParam& param, TopoInfo* topoInfo, AlgHierarchyInfo& algHierarchyInfo, | ||
| 48 | + AlgResourceRequest& resourceRequest, AlgType& algType) override; | ||
| 49 | + | ||
| 50 | +private: | ||
| 51 | + HcclResult SelectAndPrepareBirsVTemplate( | ||
| 52 | + bool isSingleServer, u32 localRank, u32 localRankSize, std::unique_ptr<AlgTemplateBase>& templatePtr); | ||
| 53 | + HcclResult KernelRunLevel0(const OpParam& param, ExecMem& execMem); | ||
| 54 | + HcclResult KernelRun(const OpParam& param, ExecMem& execMem) override; | ||
| 55 | +}; | ||
| 56 | + | ||
| 57 | +} // namespace ops_hccl_experimental | ||
| 58 | + | ||
| 59 | +#endif | ||
| @@ -0,0 +1,48 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 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 | +#include "reduce_scatter_v_birs_selector.h" | ||
| 11 | + | ||
| 12 | +namespace ops_hccl_experimental { | ||
| 13 | +using ops_hccl::TopoInfo; | ||
| 14 | + | ||
| 15 | +BirsVSelectResult DecideReduceScatterVBirsAlg(const TopoInfo& topoInfo, std::string& algName) | ||
| 16 | +{ | ||
| 17 | + if (topoInfo.userRankSize == 1) { | ||
| 18 | + return BirsVSelectResult::vRejectRankSizeOne; | ||
| 19 | + } | ||
| 20 | + if (topoInfo.deviceType != HcclDevType::DEV_TYPE_910_93 || (topoInfo.userRankSize % 2 != 0)) { | ||
| 21 | + return BirsVSelectResult::vNotSelected; | ||
| 22 | + } | ||
| 23 | + if (topoInfo.serverNum == 0) { | ||
| 24 | + return BirsVSelectResult::vRejectServerNumZero; | ||
| 25 | + } | ||
| 26 | + if (topoInfo.userRankSize / topoInfo.serverNum < 4) { | ||
| 27 | + return BirsVSelectResult::vRejectRanksPerServerLT4; | ||
| 28 | + } | ||
| 29 | + algName = "ReduceScatterVBIRSExecutor"; | ||
| 30 | + return BirsVSelectResult::vSelected; | ||
| 31 | +} | ||
| 32 | + | ||
| 33 | +HcclResult BirsVSelectResultToCode(BirsVSelectResult result) | ||
| 34 | +{ | ||
| 35 | + switch (result) { | ||
| 36 | + case BirsVSelectResult::vSelected: | ||
| 37 | + case BirsVSelectResult::vNotSelected: | ||
| 38 | + return HCCL_SUCCESS; | ||
| 39 | + case BirsVSelectResult::vRejectRankSizeOne: | ||
| 40 | + return HCCL_E_INTERNAL; | ||
| 41 | + case BirsVSelectResult::vRejectServerNumZero: | ||
| 42 | + case BirsVSelectResult::vRejectRanksPerServerLT4: | ||
| 43 | + return HCCL_E_PARA; | ||
| 44 | + } | ||
| 45 | + return HCCL_E_INTERNAL; | ||
| 46 | +} | ||
| 47 | + | ||
| 48 | +} // namespace ops_hccl_experimental | ||
| @@ -0,0 +1,30 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 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 | +#ifndef OPS_HCCL_EXPERIMENTAL_REDUCE_SCATTER_V_BIRS_SELECTOR | ||
| 11 | +#define OPS_HCCL_EXPERIMENTAL_REDUCE_SCATTER_V_BIRS_SELECTOR | ||
| 12 | + | ||
| 13 | +#include <string> | ||
| 14 | +#include "alg_param.h" | ||
| 15 | + | ||
| 16 | +namespace ops_hccl_experimental { | ||
| 17 | +enum class BirsVSelectResult { | ||
| 18 | + vNotSelected, | ||
| 19 | + vSelected, | ||
| 20 | + vRejectRankSizeOne, | ||
| 21 | + vRejectServerNumZero, | ||
| 22 | + vRejectRanksPerServerLT4, | ||
| 23 | +}; | ||
| 24 | + | ||
| 25 | +BirsVSelectResult DecideReduceScatterVBirsAlg(const ops_hccl::TopoInfo& topoInfo, std::string& algName); | ||
| 26 | + | ||
| 27 | +HcclResult BirsVSelectResultToCode(BirsVSelectResult result); | ||
| 28 | +} // namespace ops_hccl_experimental | ||
| 29 | + | ||
| 30 | +#endif | ||
| @@ -0,0 +1,190 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 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 "reduce_scatter_v_executor_base.h" | ||
| 12 | + | ||
| 13 | +namespace ops_hccl_experimental { | ||
| 14 | +using ops_hccl::ExecMem; | ||
| 15 | +using ops_hccl::HCCL_INTERNODE_MAX_DATA_RATE; | ||
| 16 | +using ops_hccl::RDMA_SEND_MAX_SIZE; | ||
| 17 | +using ops_hccl::SDMA_SEND_MAX_SIZE; | ||
| 18 | + | ||
| 19 | +ReduceScatterVExecutorBase::ReduceScatterVExecutorBase() : ExecutorBase() {} | ||
| 20 | + | ||
| 21 | +// 执行入口 | ||
| 22 | +HcclResult ReduceScatterVExecutorBase::Orchestrate(const OpParam& param, AlgResourceCtx* resCtx) | ||
| 23 | +{ | ||
| 24 | + HcclUs startut = TIME_NOW(); | ||
| 25 | + topoInfo_ = &(resCtx->topoInfo); | ||
| 26 | + algResource_ = resCtx; | ||
| 27 | + tag_ = std::string(param.tag); | ||
| 28 | + algType_ = resCtx->algType; | ||
| 29 | + unitSize_ = HCCL_SIZE_TABLE[param.vDataDes.dataType]; | ||
| 30 | + | ||
| 31 | + CHK_PTR_NULL(param.inputPtr); | ||
| 32 | + CHK_PTR_NULL(param.outputPtr); | ||
| 33 | + | ||
| 34 | + // 做参数的还原 | ||
| 35 | + ThreadHandle* threadHandlePtr | ||
| 36 | + = reinterpret_cast<ThreadHandle*>(reinterpret_cast<char*>(algResource_) + sizeof(AlgResourceCtx)); | ||
| 37 | + ChannelInfo* channelInfoPtr = reinterpret_cast<ChannelInfo*>( | ||
| 38 | + reinterpret_cast<char*>(threadHandlePtr) + sizeof(ThreadHandle) * (algResource_->slaveThreadNum + 1)); | ||
| 39 | + | ||
| 40 | + HCCL_DEBUG("[ReduceScatterVExecutorBase][Orchestrate] slaveThreadNum[%u]", algResource_->slaveThreadNum); | ||
| 41 | + for (u32 i = 0; i < algResource_->slaveThreadNum + 1; i++) { | ||
| 42 | + HCCL_DEBUG("[ReduceScatterVExecutorBase][Orchestrate] threadHandle[%u]=[%llu]", i, threadHandlePtr[i]); | ||
| 43 | + if (i == 0) { | ||
| 44 | + thread_ = threadHandlePtr[i]; | ||
| 45 | + } else { | ||
| 46 | + slaveThreads_.push_back(threadHandlePtr[i]); | ||
| 47 | + } | ||
| 48 | + } | ||
| 49 | + AlgHierarchyInfo& algHierarchyInfo = resCtx->algHierarchyInfo; | ||
| 50 | + channels_.resize(algHierarchyInfo.levels); | ||
| 51 | + for (u32 level = 0; level < algHierarchyInfo.levels; level++) { | ||
| 52 | + u32 curLevelRankSize = algHierarchyInfo.infos[level].localRankSize; | ||
| 53 | + channels_[level].resize(curLevelRankSize); | ||
| 54 | + for (u32 rank = 0; rank < curLevelRankSize; rank++) { | ||
| 55 | + channels_[level][rank] = channelInfoPtr[rank]; | ||
| 56 | + } | ||
| 57 | + channelInfoPtr += curLevelRankSize; | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + HcclResult ret = RunLoop(param); | ||
| 61 | + CHK_PRT_RET( | ||
| 62 | + ret != HCCL_SUCCESS, | ||
| 63 | + HCCL_ERROR( | ||
| 64 | + "[ReduceScatterVExecutorBase][Orchestrate]errNo[0x%016llx]" | ||
| 65 | + " ReduceScatterV executor kernel run failed", | ||
| 66 | + HCCL_ERROR_CODE(ret)), | ||
| 67 | + ret); | ||
| 68 | + HCCL_INFO( | ||
| 69 | + "[ReduceScatterVExecutorBase][Orchestrate]tag[%s]" | ||
| 70 | + " ReduceScatterV executor orchestrate success, take time [%lld]us.", | ||
| 71 | + param.tag, DURATION_US(TIME_NOW() - startut)); | ||
| 72 | + return HCCL_SUCCESS; | ||
| 73 | +} | ||
| 74 | + | ||
| 75 | +bool ReduceScatterVExecutorBase::IsHugeData(u64 curSize) const | ||
| 76 | +{ | ||
| 77 | + bool hugeData = curSize * topoInfo_->userRankSize / HCCL_INTERNODE_MAX_DATA_RATE > RDMA_SEND_MAX_SIZE | ||
| 78 | + || curSize > SDMA_SEND_MAX_SIZE; | ||
| 79 | + return hugeData; | ||
| 80 | +} | ||
| 81 | + | ||
| 82 | +HcclResult ReduceScatterVExecutorBase::AcquireCommIfNeeded(const OpParam& param) | ||
| 83 | +{ | ||
| 84 | + if (param.engine == CommEngine::COMM_ENGINE_CPU_TS || param.engine == CommEngine::COMM_ENGINE_CPU) { | ||
| 85 | + int32_t ret = HcommAcquireComm(param.commName); | ||
| 86 | + CHK_PRT_RET( | ||
| 87 | + ret != HCCL_SUCCESS, HCCL_ERROR("[%s] [%s] HcommAcquireComm failed ", __func__, param.commName), | ||
| 88 | + static_cast<HcclResult>(ret)); | ||
| 89 | + } | ||
| 90 | + return HCCL_SUCCESS; | ||
| 91 | +} | ||
| 92 | + | ||
| 93 | +HcclResult ReduceScatterVExecutorBase::ReleaseCommIfNeeded(const OpParam& param) | ||
| 94 | +{ | ||
| 95 | + if (param.engine == CommEngine::COMM_ENGINE_CPU_TS || param.engine == CommEngine::COMM_ENGINE_CPU) { | ||
| 96 | + int32_t ret = HcommReleaseComm(param.commName); | ||
| 97 | + CHK_PRT_RET( | ||
| 98 | + ret != HCCL_SUCCESS, HCCL_ERROR("[%s] [%s] HcommReleaseComm failed ", __func__, param.commName), | ||
| 99 | + static_cast<HcclResult>(ret)); | ||
| 100 | + } | ||
| 101 | + return HCCL_SUCCESS; | ||
| 102 | +} | ||
| 103 | + | ||
| 104 | +HcclResult ReduceScatterVExecutorBase::RunLoop(const OpParam& param) | ||
| 105 | +{ | ||
| 106 | + // V变体: 本rank接收量为outputSize/unitSize, 总发送量为inputSize | ||
| 107 | + u64 totalRecvCount = param.outputSize / unitSize_; | ||
| 108 | + u64 totalRecvSize = param.outputSize; | ||
| 109 | + | ||
| 110 | + u8* curUserInputPtr = static_cast<u8*>(param.inputPtr); | ||
| 111 | + u8* curUserOutputPtr = static_cast<u8*>(param.outputPtr); | ||
| 112 | + auto cclInputMem = algResource_->cclInputMem; | ||
| 113 | + auto cclOutputMem = algResource_->cclOutputMem; | ||
| 114 | + CHK_PRT_RET( | ||
| 115 | + (cclInputMem.size == 0), HCCL_ERROR("[ReduceScatterVExecutorBase][RunLoop]cclBuffer size is zero"), | ||
| 116 | + HCCL_E_PARA); | ||
| 117 | + | ||
| 118 | + CHK_RET(AcquireCommIfNeeded(param)); | ||
| 119 | + | ||
| 120 | + u64 curRecvCount = totalRecvCount; | ||
| 121 | + u64 curRecvSize = totalRecvSize; | ||
| 122 | + // V变体: 发送总量为inputSize (按各rank的sendCounts/sendDispls布局) | ||
| 123 | + u64 curSendSize = param.inputSize; | ||
| 124 | + | ||
| 125 | +#ifndef AICPU_COMPILE | ||
| 126 | + if (!IsHugeData(curRecvSize)) { | ||
| 127 | + CHK_RET(static_cast<HcclResult>(HcommBatchModeStart(param.algTag))); | ||
| 128 | + } | ||
| 129 | +#endif | ||
| 130 | + // RunLoop 构造 curInputMem 前校验容量 | ||
| 131 | + CHK_PRT_RET( | ||
| 132 | + curSendSize > cclInputMem.size, | ||
| 133 | + HCCL_ERROR("[RunLoop]inputSize[%llu] exceeds cclInputMem size[%llu]", curSendSize, cclInputMem.size), | ||
| 134 | + HCCL_E_PARA); | ||
| 135 | + // scratch 实际可用区域为整个 cclInputMem(CCL buffer 前半区), 声明大小必须传真实容量而非 curSendSize: | ||
| 136 | + // 模板以 rankSize*localStrideSize(按 max 切片对齐)为 scratch 需求, 小消息时该需求可大于 inputSize, | ||
| 137 | + // 若按 curSendSize 声明会使 CheckScratchCapacity 误报容量不足 | ||
| 138 | + HcclMem curInputMem{cclInputMem.type, cclInputMem.addr, cclInputMem.size}; | ||
| 139 | + HcclMem curOutputMem{cclOutputMem.type, cclOutputMem.addr, curRecvSize}; | ||
| 140 | + | ||
| 141 | + ExecMem execMem; | ||
| 142 | + execMem.count = curRecvCount; | ||
| 143 | + execMem.inputMem = curInputMem; | ||
| 144 | + execMem.outputMem = curOutputMem; | ||
| 145 | + execMem.inputPtr = curUserInputPtr; | ||
| 146 | + execMem.outputPtr = curUserOutputPtr; | ||
| 147 | + | ||
| 148 | + HCCL_DEBUG( | ||
| 149 | + "[ReduceScatterVExecutorBase][RunLoop] curUserInputPtr[%p], curUserOutputPtr[%p]," | ||
| 150 | + " curRecvCount[%llu], curRecvSize[%llu], curSendSize[%llu], inputPtr[%p], outputPtr[%p]", | ||
| 151 | + curUserInputPtr, curUserOutputPtr, curRecvCount, curRecvSize, curSendSize, curInputMem.addr, curOutputMem.addr); | ||
| 152 | + | ||
| 153 | + CHK_RET(KernelRun(param, execMem)); | ||
| 154 | + | ||
| 155 | +#ifndef AICPU_COMPILE | ||
| 156 | + if (!IsHugeData(curRecvSize)) { | ||
| 157 | + CHK_RET(static_cast<HcclResult>(HcommBatchModeEnd(param.algTag))); | ||
| 158 | + } | ||
| 159 | +#endif | ||
| 160 | + CHK_RET(ReleaseCommIfNeeded(param)); | ||
| 161 | + return HCCL_SUCCESS; | ||
| 162 | +} | ||
| 163 | + | ||
| 164 | +HcclResult ReduceScatterVExecutorBase::CalcResRequest( | ||
| 165 | + HcclComm comm, const OpParam& param, TopoInfo* topoInfo, AlgHierarchyInfo& algHierarchyInfo, | ||
| 166 | + AlgResourceRequest& resourceRequest, AlgType& algType) | ||
| 167 | +{ | ||
| 168 | + return HCCL_SUCCESS; | ||
| 169 | +} | ||
| 170 | + | ||
| 171 | +HcclResult ReduceScatterVExecutorBase::PrepareDataSliceV( | ||
| 172 | + const u64* sendCounts, const u64* sendDispls, u32 unitSize, u32 sliceNum, std::vector<Slice>& dataSlice) | ||
| 173 | +{ | ||
| 174 | + CHK_PRT_RET( | ||
| 175 | + (sliceNum == 0), HCCL_ERROR("[ReduceScatterVExecutorBase][PrepareDataSliceV]sliceNum is zero."), HCCL_E_PARA); | ||
| 176 | + | ||
| 177 | + CHK_PTR_NULL(sendCounts); | ||
| 178 | + CHK_PTR_NULL(sendDispls); | ||
| 179 | + dataSlice.resize(sliceNum); | ||
| 180 | + for (u32 i = 0; i < sliceNum; i++) { | ||
| 181 | + dataSlice[i].size = sendCounts[i] * unitSize; | ||
| 182 | + dataSlice[i].offset = sendDispls[i] * unitSize; | ||
| 183 | + HCCL_DEBUG( | ||
| 184 | + "[ReduceScatterVExecutorBase][PrepareDataSliceV] slice[%u]: offset[%llu] size[%llu]", i, | ||
| 185 | + dataSlice[i].offset, dataSlice[i].size); | ||
| 186 | + } | ||
| 187 | + return HCCL_SUCCESS; | ||
| 188 | +} | ||
| 189 | + | ||
| 190 | +} // namespace ops_hccl_experimental | ||
| @@ -0,0 +1,66 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 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 | +#ifndef REDUCE_SCATTER_V_EXECUTOR_H | ||
| 12 | +#define REDUCE_SCATTER_V_EXECUTOR_H | ||
| 13 | + | ||
| 14 | +#include "alg_param.h" | ||
| 15 | +#include "executor_base.h" | ||
| 16 | +#include "config_log.h" | ||
| 17 | +#include "alg_template_base_experimental.h" | ||
| 18 | + | ||
| 19 | +namespace ops_hccl_experimental { | ||
| 20 | +using ops_hccl::AlgHierarchyInfo; | ||
| 21 | +using ops_hccl::AlgResourceCtx; | ||
| 22 | +using ops_hccl::AlgResourceRequest; | ||
| 23 | +using ops_hccl::AlgType; | ||
| 24 | +using ops_hccl::ChannelInfo; | ||
| 25 | +using ops_hccl::ExecutorBase; | ||
| 26 | +using ops_hccl::OpParam; | ||
| 27 | +using ops_hccl::Slice; | ||
| 28 | +using ops_hccl::TopoInfo; | ||
| 29 | + | ||
| 30 | +// BIRS-V 线程数单一来源: executor 侧资源申请(slaveThreadNum/notify)与模板侧 notify 同步必须一致, | ||
| 31 | +// 两侧统一引用本常量, 禁止再各自定义 | ||
| 32 | +constexpr u32 BIRS_THREAD_NUM = 2; | ||
| 33 | + | ||
| 34 | +class ReduceScatterVExecutorBase : public ExecutorBase { | ||
| 35 | +public: | ||
| 36 | + explicit ReduceScatterVExecutorBase(); | ||
| 37 | + ~ReduceScatterVExecutorBase() override = default; | ||
| 38 | + | ||
| 39 | + HcclResult Orchestrate(const OpParam& param, AlgResourceCtx* resCtx) override; | ||
| 40 | + | ||
| 41 | + /* *************** 资源计算 *************** */ | ||
| 42 | + HcclResult CalcResRequest( | ||
| 43 | + HcclComm comm, const OpParam& param, TopoInfo* topoInfo, AlgHierarchyInfo& algHierarchyInfo, | ||
| 44 | + AlgResourceRequest& resourceRequest, AlgType& algType) override; | ||
| 45 | + | ||
| 46 | +protected: | ||
| 47 | + // 用于需要Loop的Executor | ||
| 48 | + virtual HcclResult RunLoop(const OpParam& param); | ||
| 49 | + | ||
| 50 | + /* *************** 通用工具 *************** */ | ||
| 51 | + // 根据变长counts/displs准备每个rank的数据切片(变长) | ||
| 52 | + HcclResult PrepareDataSliceV( | ||
| 53 | + const u64* sendCounts, const u64* sendDispls, u32 unitSize, u32 sliceNum, std::vector<Slice>& dataSlice); | ||
| 54 | + bool IsHugeData(u64 curSize) const; | ||
| 55 | + HcclResult AcquireCommIfNeeded(const OpParam& param); | ||
| 56 | + HcclResult ReleaseCommIfNeeded(const OpParam& param); | ||
| 57 | + | ||
| 58 | + ThreadHandle thread_ = 0; | ||
| 59 | + std::vector<ThreadHandle> slaveThreads_; | ||
| 60 | + std::vector<std::vector<ChannelInfo>> channels_; | ||
| 61 | + u32 unitSize_; | ||
| 62 | +}; | ||
| 63 | + | ||
| 64 | +} // namespace ops_hccl_experimental | ||
| 65 | + | ||
| 66 | +#endif | ||
| @@ -0,0 +1,19 @@ | |||
| 1 | +# ----------------------------------------------------------------------------------------------------------- | ||
| 2 | +# Copyright (c) 2026 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 | +set(src_list | ||
| 11 | + ${CMAKE_CURRENT_SOURCE_DIR}/reduce_scatter_v_birs_inter.cc | ||
| 12 | + ${CMAKE_CURRENT_SOURCE_DIR}/reduce_scatter_v_birs.cc | ||
| 13 | +) | ||
| 14 | + | ||
| 15 | +if(TARGET hccl) | ||
| 16 | + target_sources(hccl PRIVATE | ||
| 17 | + ${src_list} | ||
| 18 | + ) | ||
| 19 | +endif() | ||
| @@ -0,0 +1,304 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | +#include "alg_template_register.h" | ||
| 12 | +#include "reduce_scatter_v_birs.h" | ||
| 13 | +#include "reduce_scatter_v_executor_base.h" | ||
| 14 | +#include <algorithm> | ||
| 15 | + | ||
| 16 | +namespace ops_hccl_experimental { | ||
| 17 | +ReduceScatterVBIRS::ReduceScatterVBIRS() : AlgTemplateBaseExperimental() {} | ||
| 18 | + | ||
| 19 | +ReduceScatterVBIRS::~ReduceScatterVBIRS() {} | ||
| 20 | + | ||
| 21 | +HcclResult ReduceScatterVBIRS::Prepare(u32 interRank, u32 interRankSize) | ||
| 22 | +{ | ||
| 23 | + interRank_ = interRank; | ||
| 24 | + interRankSize_ = interRankSize; | ||
| 25 | + return HCCL_SUCCESS; | ||
| 26 | +} | ||
| 27 | + | ||
| 28 | +HcclResult ReduceScatterVBIRS::Prepare( | ||
| 29 | + HcclMem& inputMem, HcclMem& outputMem, HcclMem& scratchMem, const u64 count, const HcclDataType dataType, | ||
| 30 | + ThreadHandle thread, const std::vector<ThreadHandle>& slaveThreads, const HcclReduceOp reductionOp, const u32 root, | ||
| 31 | + const std::vector<Slice>& slices, const u64 baseOffset, const bool disableDMAReduce) | ||
| 32 | +{ | ||
| 33 | + mainThread = thread; | ||
| 34 | + subThreads = slaveThreads; | ||
| 35 | + AlgTemplateBase::Prepare( | ||
| 36 | + inputMem, outputMem, scratchMem, count, dataType, thread, reductionOp, root, slices, baseOffset, | ||
| 37 | + disableDMAReduce); | ||
| 38 | + return HCCL_SUCCESS; | ||
| 39 | +} | ||
| 40 | + | ||
| 41 | +HcclResult ReduceScatterVBIRS::LocalReduceCCLToCCL(u64 srcOffset, u64 dstOffset, u64 size, ThreadHandle thread) | ||
| 42 | +{ | ||
| 43 | + void* srcSlice = static_cast<void*>(static_cast<u8*>(scratchMem_.addr) + srcOffset); | ||
| 44 | + void* dstSlice = static_cast<void*>(static_cast<u8*>(scratchMem_.addr) + dstOffset); | ||
| 45 | + CHK_RET(static_cast<HcclResult>(HcommLocalReduceOnThread( | ||
| 46 | + thread, dstSlice, srcSlice, size / unitSize, static_cast<HcommDataType>(dataType_), | ||
| 47 | + static_cast<HcommReduceOp>(reductionOp_)))); | ||
| 48 | + return HCCL_SUCCESS; | ||
| 49 | +} | ||
| 50 | + | ||
| 51 | +void ReduceScatterVBIRS::CalcLocalStrideSize(const u32 rankSize) | ||
| 52 | +{ | ||
| 53 | + u64 maxSliceSize = 0; | ||
| 54 | + for (u32 i = 0; i < rankSize; i++) { | ||
| 55 | + maxSliceSize = std::max(maxSliceSize, slices_[i].size); | ||
| 56 | + } | ||
| 57 | + localStrideSize = RoundUpWithDivisor(maxSliceSize, HCCL_MIN_SLICE_ALIGN_910B); | ||
| 58 | +} | ||
| 59 | + | ||
| 60 | +HcclResult ReduceScatterVBIRS::CheckScratchCapacity(u32 rankSize) | ||
| 61 | +{ | ||
| 62 | + u64 scratchDemand = static_cast<u64>(rankSize) * localStrideSize; | ||
| 63 | + CHK_PRT_RET( | ||
| 64 | + scratchDemand > scratchMem_.size, | ||
| 65 | + HCCL_ERROR( | ||
| 66 | + "[ReduceScatterVBIRS][CheckScratchCapacity]scratch demand[%llu] exceeds scratchMem size[%llu]", | ||
| 67 | + scratchDemand, scratchMem_.size), | ||
| 68 | + HCCL_E_PARA); | ||
| 69 | + return HCCL_SUCCESS; | ||
| 70 | +} | ||
| 71 | + | ||
| 72 | +void ReduceScatterVBIRS::GetNotifyIdxMainToSub(std::vector<u32>& notifyIdxMainToSub) | ||
| 73 | +{ | ||
| 74 | + notifyIdxMainToSub.clear(); | ||
| 75 | + u32 slaveThreadNum = BIRS_THREAD_NUM; | ||
| 76 | + for (u32 slaveThreadIdx = 0; slaveThreadIdx < slaveThreadNum; slaveThreadIdx++) { | ||
| 77 | + notifyIdxMainToSub.push_back(0); | ||
| 78 | + } | ||
| 79 | +} | ||
| 80 | + | ||
| 81 | +void ReduceScatterVBIRS::GetNotifyIdxSubToMain(std::vector<u32>& notifyIdxSubToMain) | ||
| 82 | +{ | ||
| 83 | + notifyIdxSubToMain.clear(); | ||
| 84 | + u32 notifyNum = BIRS_THREAD_NUM; | ||
| 85 | + for (u32 notifyIdx = 0; notifyIdx < notifyNum; notifyIdx++) { | ||
| 86 | + notifyIdxSubToMain.push_back(notifyIdx); | ||
| 87 | + } | ||
| 88 | +} | ||
| 89 | + | ||
| 90 | +HcclResult ReduceScatterVBIRS::PrepareSlicesData(const u32 unitSize, const u64 totalCount, const u32 rankSize) | ||
| 91 | +{ | ||
| 92 | + // V变体: 切片由executor按sendCounts/sendDispls提前构造并经Prepare传入, 此处仅作兜底(均匀) | ||
| 93 | + slices_.resize(rankSize); | ||
| 94 | + u64 sliceSize = totalCount * unitSize; | ||
| 95 | + | ||
| 96 | + for (u32 i = 0; i < rankSize; i++) { | ||
| 97 | + slices_[i].offset = i * sliceSize; | ||
| 98 | + slices_[i].size = sliceSize; | ||
| 99 | + HCCL_DEBUG(" default slice[%u]: offset: [%llu] size[%llu]", i, i * sliceSize, sliceSize); | ||
| 100 | + } | ||
| 101 | + return HCCL_SUCCESS; | ||
| 102 | +} | ||
| 103 | + | ||
| 104 | +HcclResult ReduceScatterVBIRS::Preprocess(const u32 rank, const u32 rankSize, std::vector<ChannelInfo>& channels) | ||
| 105 | +{ | ||
| 106 | + if (rankSize == 1) { | ||
| 107 | + if (inputMem_.addr != outputMem_.addr) { | ||
| 108 | + // V变体: 本rank接收量为 slices_[rank].size | ||
| 109 | + CHK_RET(static_cast<HcclResult>(HcommLocalCopyOnThread( | ||
| 110 | + thread_, outputMem_.addr, static_cast<u8*>(inputMem_.addr) + slices_[rank].offset, | ||
| 111 | + slices_[rank].size))); | ||
| 112 | + } | ||
| 113 | + return HCCL_SUCCESS; | ||
| 114 | + } | ||
| 115 | + | ||
| 116 | + if (channels.size() < rankSize) { | ||
| 117 | + HCCL_ERROR( | ||
| 118 | + "[ReduceScatterVBIRS][RunAsync]rank[%u] linksize[%llu] is less than rankSize[%u]", rank, channels.size(), | ||
| 119 | + rankSize); | ||
| 120 | + return HCCL_E_INTERNAL; | ||
| 121 | + } | ||
| 122 | + | ||
| 123 | + unitSize = DataUnitSize(dataType_); | ||
| 124 | + if (unitSize == 0) { | ||
| 125 | + HCCL_ERROR("[ReduceScatterVBIRS][RunAsync]rank[%u] unit data size is zero", rank); | ||
| 126 | + return HCCL_E_INTERNAL; | ||
| 127 | + } | ||
| 128 | + if (slices_.size() == 0) { | ||
| 129 | + CHK_RET(PrepareSlicesData(unitSize, count_, rankSize)); | ||
| 130 | + } | ||
| 131 | + return HCCL_SUCCESS; | ||
| 132 | +} | ||
| 133 | + | ||
| 134 | +HcclResult ReduceScatterVBIRS::HCCSProcessMainLoop( | ||
| 135 | + u32 round, const u32 rank, const u32 rankSize, u32 rankSizeX_, u64 localStrideSize) | ||
| 136 | +{ | ||
| 137 | + if (round != 0) { | ||
| 138 | + CHK_RET(static_cast<HcclResult>( | ||
| 139 | + HcommChannelNotifyRecordOnThread(subThreads[0], hccs_links[round - 1].handle, NOTIFY_IDX_ACK))); | ||
| 140 | + CHK_RET(static_cast<HcclResult>(HcommChannelNotifyWaitOnThread( | ||
| 141 | + subThreads[0], hccs_links_reversed[round - 1].handle, NOTIFY_IDX_ACK, CUSTOM_TIMEOUT))); | ||
| 142 | + u64 localOffsetByte = hccs_ranks[round - 1] / rankSizeX_ * localStrideSize; | ||
| 143 | + u64 remoteOffsetByte = ((rankSize / rankSizeX_) + rank / rankSizeX_) * localStrideSize; | ||
| 144 | + void* src = static_cast<void*>(static_cast<u8*>(scratchMem_.addr) + localOffsetByte); | ||
| 145 | + void* dst = static_cast<void*>(static_cast<u8*>(hccs_links[round - 1].remoteOutput.addr) + remoteOffsetByte); | ||
| 146 | + | ||
| 147 | + // V变体: 传输量为目标rank(hccs_ranks[round-1])对应的变长切片大小 | ||
| 148 | + u64 curSliceSize = slices_[hccs_ranks[round - 1]].size; | ||
| 149 | + CHK_RET(static_cast<HcclResult>( | ||
| 150 | + HcommWriteOnThread(subThreads[0], hccs_links[round - 1].handle, dst, src, curSliceSize))); | ||
| 151 | + | ||
| 152 | + CHK_RET(static_cast<HcclResult>( | ||
| 153 | + HcommChannelNotifyRecordOnThread(subThreads[0], hccs_links[round - 1].handle, NOTIFY_IDX_DATA_SIGNAL))); | ||
| 154 | + CHK_RET(static_cast<HcclResult>(HcommChannelNotifyWaitOnThread( | ||
| 155 | + subThreads[0], hccs_links_reversed[round - 1].handle, NOTIFY_IDX_DATA_SIGNAL, CUSTOM_TIMEOUT))); | ||
| 156 | + } | ||
| 157 | + return HCCL_SUCCESS; | ||
| 158 | +} | ||
| 159 | + | ||
| 160 | +HcclResult ReduceScatterVBIRS::SIOProcessMainLoop( | ||
| 161 | + u32 round, const u32 rank, const u32 rankSize, u32 rankSizeX_, u64 localStrideSize) | ||
| 162 | +{ | ||
| 163 | + CHK_RET(static_cast<HcclResult>(HcommChannelNotifyRecordOnThread(mainThread, sio_link.handle, NOTIFY_IDX_ACK))); | ||
| 164 | + CHK_RET(static_cast<HcclResult>( | ||
| 165 | + HcommChannelNotifyWaitOnThread(mainThread, sio_link.handle, NOTIFY_IDX_ACK, CUSTOM_TIMEOUT))); | ||
| 166 | + | ||
| 167 | + // V变体: 本轮处理的target rank及其变长切片 | ||
| 168 | + u32 targetRank = (round != hccs_ranks.size()) ? hccs_neighbour_rank[round] : sio_rank; | ||
| 169 | + u64 localOffsetByte = slices_[targetRank].offset; | ||
| 170 | + u64 remoteOffsetByte = (round != hccs_ranks.size()) ? (hccs_ranks[round] / rankSizeX_ * localStrideSize) : | ||
| 171 | + rank / rankSizeX_ * localStrideSize; | ||
| 172 | + void* src = static_cast<void*>(static_cast<u8*>(inputMem_.addr) + localOffsetByte); | ||
| 173 | + void* dst = static_cast<void*>(static_cast<u8*>(sio_link.remoteOutput.addr) + remoteOffsetByte); | ||
| 174 | + | ||
| 175 | + u64 curSliceSize = slices_[targetRank].size; | ||
| 176 | + CHK_RET(static_cast<HcclResult>(HcommWriteReduceOnThread( | ||
| 177 | + mainThread, sio_link.handle, dst, src, curSliceSize / unitSize, static_cast<HcommDataType>(dataType_), | ||
| 178 | + static_cast<HcommReduceOp>(reductionOp_)))); | ||
| 179 | + | ||
| 180 | + CHK_RET( | ||
| 181 | + static_cast<HcclResult>(HcommChannelNotifyRecordOnThread(mainThread, sio_link.handle, NOTIFY_IDX_DATA_SIGNAL))); | ||
| 182 | + CHK_RET(static_cast<HcclResult>( | ||
| 183 | + HcommChannelNotifyWaitOnThread(mainThread, sio_link.handle, NOTIFY_IDX_DATA_SIGNAL, CUSTOM_TIMEOUT))); | ||
| 184 | + | ||
| 185 | + return HCCL_SUCCESS; | ||
| 186 | +} | ||
| 187 | + | ||
| 188 | +HcclResult ReduceScatterVBIRS::LocalCopyMainLoop( | ||
| 189 | + u32 round, const u32 rank, const u32 rankSize, u32 rankSizeX_, u64 localStrideSize) | ||
| 190 | +{ | ||
| 191 | + if (round < hccs_ranks.size()) { | ||
| 192 | + u32 rank_idx = (round < hccs_ranks.size() - 1) ? hccs_ranks[round + 1] : rank; | ||
| 193 | + void* srcSlice = static_cast<void*>(static_cast<u8*>(inputMem_.addr) + slices_[rank_idx].offset); | ||
| 194 | + void* dstSlice | ||
| 195 | + = static_cast<void*>(static_cast<u8*>(scratchMem_.addr) + rank_idx / rankSizeX_ * localStrideSize); | ||
| 196 | + // V变体: 拷贝量为rank_idx对应的变长切片大小 | ||
| 197 | + u64 curSliceSize = slices_[rank_idx].size; | ||
| 198 | + CHK_RET(static_cast<HcclResult>(HcommLocalCopyOnThread(subThreads[1], dstSlice, srcSlice, curSliceSize))); | ||
| 199 | + } | ||
| 200 | + return HCCL_SUCCESS; | ||
| 201 | +} | ||
| 202 | + | ||
| 203 | +HcclResult ReduceScatterVBIRS::FinalStep(const u32 rank, const u32 rankSize, u32 rankSizeX_, u64 localStrideSize) | ||
| 204 | +{ | ||
| 205 | + // V变体: 本rank最终接收量为 slices_[rank].size | ||
| 206 | + u64 localSliceSize = slices_[rank].size; | ||
| 207 | + std::vector<u32> vec; | ||
| 208 | + for (u32 i = 0; i < (rankSize / rankSizeX_); i++) { | ||
| 209 | + if (i == (rank / rankSizeX_)) { | ||
| 210 | + vec.push_back((rank / rankSizeX_) * localStrideSize); | ||
| 211 | + } else { | ||
| 212 | + vec.push_back(((rankSize / rankSizeX_) + i) * localStrideSize); | ||
| 213 | + } | ||
| 214 | + } | ||
| 215 | + // Tree local reduce | ||
| 216 | + auto ind = rankSize / rankSizeX_; | ||
| 217 | + for (u32 stride = 1; stride < ind; stride *= 2) { | ||
| 218 | + for (u32 i = stride; i < ind; i += stride * 2) { | ||
| 219 | + CHK_RET(LocalReduceCCLToCCL(vec[i], vec[i - stride], localSliceSize, mainThread)); | ||
| 220 | + } | ||
| 221 | + } | ||
| 222 | + // Local copy to output | ||
| 223 | + void* srcSlice = static_cast<void*>(static_cast<u8*>(scratchMem_.addr) + vec[0]); | ||
| 224 | + void* dstSlice = static_cast<void*>(static_cast<u8*>(outputMem_.addr)); | ||
| 225 | + CHK_RET(static_cast<HcclResult>(HcommLocalCopyOnThread(mainThread, dstSlice, srcSlice, localSliceSize))); | ||
| 226 | + return HCCL_SUCCESS; | ||
| 227 | +} | ||
| 228 | + | ||
| 229 | +HcclResult ReduceScatterVBIRS::RunAsync(const u32 rank, const u32 rankSize, std::vector<ChannelInfo>& channels) | ||
| 230 | +{ | ||
| 231 | + HCCL_INFO( | ||
| 232 | + "ReduceScatterVBIRS run: rank[%u] rankSize[%u] inputMem[%p] to outputMem[%p] count[%llu]", rank, rankSize, | ||
| 233 | + inputMem_.addr, outputMem_.addr, count_); | ||
| 234 | + | ||
| 235 | + hccs_ranks.clear(); | ||
| 236 | + hccs_neighbour_rank.clear(); | ||
| 237 | + hccs_links.clear(); | ||
| 238 | + | ||
| 239 | + CHK_RET(Preprocess(rank, rankSize, channels)); | ||
| 240 | + | ||
| 241 | + if (rankSize % rankSizeX_ != 0) { | ||
| 242 | + HCCL_ERROR( | ||
| 243 | + "[ReduceScatterVBIRS][RunAsync]rankSize[%u] is not evenly divisible by rankSizeX_[%u]", rankSize, | ||
| 244 | + rankSizeX_); | ||
| 245 | + return HCCL_E_INTERNAL; | ||
| 246 | + } | ||
| 247 | + | ||
| 248 | + sio_rank = rank ^ 1; | ||
| 249 | + sio_link = channels[sio_rank]; | ||
| 250 | + | ||
| 251 | + for (u32 i = 1; i < rankSize / rankSizeX_; ++i) { | ||
| 252 | + u32 current_hccs_rank = (rank + rankSizeX_ * i) % rankSize; | ||
| 253 | + hccs_ranks.push_back(current_hccs_rank); | ||
| 254 | + hccs_neighbour_rank.push_back(current_hccs_rank ^ 1); | ||
| 255 | + hccs_links.push_back(channels[hccs_ranks[i - 1]]); | ||
| 256 | + } | ||
| 257 | + hccs_links_reversed.assign(hccs_links.rbegin(), hccs_links.rend()); | ||
| 258 | + | ||
| 259 | + CalcLocalStrideSize(rankSize); | ||
| 260 | + | ||
| 261 | + CHK_RET(CheckScratchCapacity(rankSize)); | ||
| 262 | + | ||
| 263 | + // MainRecordSub + SubWaitMain | ||
| 264 | + GetNotifyIdxMainToSub(notifyIdxMainToSub_); | ||
| 265 | + CHK_RET(PreSyncInterThreads(mainThread, subThreads, notifyIdxMainToSub_)); | ||
| 266 | + | ||
| 267 | + // V变体: 初始拷贝量取 hccs_ranks[0] 对应的变长切片 | ||
| 268 | + if (!hccs_ranks.empty()) { | ||
| 269 | + u64 initSliceSize = slices_[hccs_ranks[0]].size; | ||
| 270 | + void* srcSlice = static_cast<void*>(static_cast<u8*>(inputMem_.addr) + slices_[hccs_ranks[0]].offset); | ||
| 271 | + void* dstSlice | ||
| 272 | + = static_cast<void*>(static_cast<u8*>(scratchMem_.addr) + hccs_ranks[0] / rankSizeX_ * localStrideSize); | ||
| 273 | + CHK_RET(static_cast<HcclResult>(HcommLocalCopyOnThread(mainThread, dstSlice, srcSlice, initSliceSize))); | ||
| 274 | + } | ||
| 275 | + | ||
| 276 | + GetNotifyIdxSubToMain(notifyIdxSubToMain_); | ||
| 277 | + CHK_RET(PostSyncInterThreads(mainThread, subThreads, notifyIdxSubToMain_)); | ||
| 278 | + for (u32 round = 0; round < hccs_ranks.size() + 1; round++) { | ||
| 279 | + // MainRecordSub + SubWaitMain | ||
| 280 | + CHK_RET(PreSyncInterThreads(mainThread, subThreads, notifyIdxMainToSub_)); | ||
| 281 | + | ||
| 282 | + CHK_RET(HCCSProcessMainLoop(round, rank, rankSize, rankSizeX_, localStrideSize)); | ||
| 283 | + | ||
| 284 | + CHK_RET(SIOProcessMainLoop(round, rank, rankSize, rankSizeX_, localStrideSize)); | ||
| 285 | + | ||
| 286 | + CHK_RET(LocalCopyMainLoop(round, rank, rankSize, rankSizeX_, localStrideSize)); | ||
| 287 | + | ||
| 288 | + // SubRecordMain + MainWaitSub | ||
| 289 | + CHK_RET(PostSyncInterThreads(mainThread, subThreads, notifyIdxSubToMain_)); | ||
| 290 | + } | ||
| 291 | + | ||
| 292 | + // MainRecordSub + SubWaitMain | ||
| 293 | + CHK_RET(PreSyncInterThreads(mainThread, subThreads, notifyIdxMainToSub_)); | ||
| 294 | + | ||
| 295 | + CHK_RET(FinalStep(rank, rankSize, rankSizeX_, localStrideSize)); | ||
| 296 | + | ||
| 297 | + CHK_RET(PostSyncInterThreads(mainThread, subThreads, notifyIdxSubToMain_)); | ||
| 298 | + | ||
| 299 | + HCCL_INFO("ReduceScatterVBIRS finished: rank[%u]", rank); | ||
| 300 | + return HCCL_SUCCESS; | ||
| 301 | +} | ||
| 302 | + | ||
| 303 | +REGISTER_TEMPLATE(TEMPLATE_REDUCE_SCATTER_V_BIRS, ReduceScatterVBIRS); | ||
| 304 | +} // namespace ops_hccl_experimental | ||
| @@ -0,0 +1,94 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 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 | +#ifndef REDUCE_SCATTER_V_BIRS_H | ||
| 12 | +#define REDUCE_SCATTER_V_BIRS_H | ||
| 13 | + | ||
| 14 | +#include "alg_template_base.h" | ||
| 15 | +#include "alg_data_trans_wrapper.h" | ||
| 16 | +#include "alg_template_base_experimental.h" | ||
| 17 | + | ||
| 18 | +namespace ops_hccl_experimental { | ||
| 19 | +using ops_hccl::AlgTemplateBase; | ||
| 20 | +using ops_hccl::AlgTemplateRegistry; | ||
| 21 | +using ops_hccl::ChannelInfo; | ||
| 22 | +using ops_hccl::CUSTOM_TIMEOUT; | ||
| 23 | +using ops_hccl::DefaultTemplateCreator; | ||
| 24 | +using ops_hccl::HCCL_MIN_SLICE_ALIGN_910B; | ||
| 25 | +using ops_hccl::NOTIFY_IDX_ACK; | ||
| 26 | +using ops_hccl::NOTIFY_IDX_DATA_SIGNAL; | ||
| 27 | +using ops_hccl::PostSyncInterThreads; | ||
| 28 | +using ops_hccl::PreSyncInterThreads; | ||
| 29 | +using ops_hccl::RoundUpWithDivisor; | ||
| 30 | +using ops_hccl::Slice; | ||
| 31 | +using ops_hccl::TemplateType; | ||
| 32 | + | ||
| 33 | +// ReduceScatterV 的 BIRS 算法模板(变长: 各rank接收量可不同) | ||
| 34 | +// 与均匀 BIRS 的区别: slices_ 由外部传入变长切片(offset/size按sendDispls/sendCounts), | ||
| 35 | +// 每轮传输使用目标rank对应的 slices_[rank].size, scratch统一以 maxSliceSize 向上对齐的 localStrideSize 为步长 | ||
| 36 | +// 前置条件: comm 内所有 rank 传入一致的 sendCounts/sendDispls 数组(集合通信语义约定, 与老流程一致)。 | ||
| 37 | +// 满足该条件时, SIO 链路两端针对同一目标 rank 的 slices_[target].size 必然相等, | ||
| 38 | +// 每轮 write-reduce 两端长度一致, 对所有规约类型结果正确。 | ||
| 39 | +class ReduceScatterVBIRS : public AlgTemplateBaseExperimental { | ||
| 40 | +public: | ||
| 41 | + explicit ReduceScatterVBIRS(); | ||
| 42 | + | ||
| 43 | + ~ReduceScatterVBIRS() override; | ||
| 44 | + | ||
| 45 | + // should be called soon after template ReduceScatterVBIRS instance created | ||
| 46 | + HcclResult Prepare(u32 interRank, u32 interRankSize) override; | ||
| 47 | + | ||
| 48 | + HcclResult Prepare( | ||
| 49 | + HcclMem& inputMem, HcclMem& outputMem, HcclMem& scratchMem, const u64 count, const HcclDataType dataType, | ||
| 50 | + ThreadHandle thread, const std::vector<ThreadHandle>& slaveThreads, const HcclReduceOp reductionOp, | ||
| 51 | + const u32 root, const std::vector<Slice>& slices, const u64 baseOffset, const bool disableDMAReduce) override; | ||
| 52 | + | ||
| 53 | + HcclResult RunAsync(const u32 rank, const u32 rankSize, std::vector<ChannelInfo>& channels) override; | ||
| 54 | + | ||
| 55 | +protected: | ||
| 56 | + void GetNotifyIdxMainToSub(std::vector<u32>& notifyIdxMainToSub); | ||
| 57 | + void GetNotifyIdxSubToMain(std::vector<u32>& notifyIdxSubToMain); | ||
| 58 | + HcclResult LocalReduceCCLToCCL(u64 srcOffset, u64 dstOffset, u64 size, ThreadHandle thread); | ||
| 59 | + // V变体: scratch步长以各rank切片大小的最大值向上对齐, 保证任意rank数据放入一个步长槽 | ||
| 60 | + virtual void CalcLocalStrideSize(const u32 rankSize); | ||
| 61 | + virtual HcclResult CheckScratchCapacity(u32 rankSize); | ||
| 62 | + | ||
| 63 | + virtual HcclResult Preprocess(const u32 rank, const u32 rankSize, std::vector<ChannelInfo>& channels); | ||
| 64 | + HcclResult PrepareSlicesData(const u32 unitSize, const u64 totalCount, const u32 rankSize); | ||
| 65 | + // 主循环各步骤: localStrideSize 为 scratch 统一步长, 各rank实际传输量取自 slices_[target].size | ||
| 66 | + HcclResult HCCSProcessMainLoop(u32 round, const u32 rank, const u32 rankSize, u32 rankSizeX_, u64 localStrideSize); | ||
| 67 | + HcclResult SIOProcessMainLoop(u32 round, const u32 rank, const u32 rankSize, u32 rankSizeX_, u64 localStrideSize); | ||
| 68 | + HcclResult LocalCopyMainLoop(u32 round, const u32 rank, const u32 rankSize, u32 rankSizeX_, u64 localStrideSize); | ||
| 69 | + HcclResult FinalStep(const u32 rank, const u32 rankSize, u32 rankSizeX_, u64 localStrideSize); | ||
| 70 | + | ||
| 71 | + u32 interRank_; // comm内的rank排序 | ||
| 72 | + u32 interRankSize_; // 本comm内ranksize总数 | ||
| 73 | + u32 sio_rank; | ||
| 74 | + std::vector<u32> hccs_ranks; | ||
| 75 | + std::vector<u32> hccs_neighbour_rank; | ||
| 76 | + | ||
| 77 | + std::vector<ChannelInfo> hccs_links; | ||
| 78 | + std::vector<ChannelInfo> hccs_links_reversed; | ||
| 79 | + ChannelInfo sio_link; | ||
| 80 | + | ||
| 81 | + u64 localStrideSize; | ||
| 82 | + | ||
| 83 | + u32 unitSize; | ||
| 84 | + u32 rankSizeX_ = 2; | ||
| 85 | + ThreadHandle mainThread; | ||
| 86 | + std::vector<ThreadHandle> subThreads; | ||
| 87 | + std::vector<u32> notifyIdxMainToSub_; | ||
| 88 | + std::vector<u32> notifyIdxSubToMain_; | ||
| 89 | + | ||
| 90 | +private: | ||
| 91 | +}; | ||
| 92 | + | ||
| 93 | +} // namespace ops_hccl_experimental | ||
| 94 | +#endif /* * REDUCE_SCATTER_V_BIRS_H */ | ||
| @@ -0,0 +1,352 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | +#include "alg_template_register.h" | ||
| 12 | +#include "reduce_scatter_v_birs_inter.h" | ||
| 13 | +#include <algorithm> | ||
| 14 | + | ||
| 15 | +namespace ops_hccl_experimental { | ||
| 16 | + | ||
| 17 | +ReduceScatterVBIRSInter::ReduceScatterVBIRSInter() : ReduceScatterVBIRS() {} | ||
| 18 | + | ||
| 19 | +ReduceScatterVBIRSInter::~ReduceScatterVBIRSInter() {} | ||
| 20 | + | ||
| 21 | +HcclResult ReduceScatterVBIRSInter::Prepare(u32 serverNum, u32 interRankSize) | ||
| 22 | +{ | ||
| 23 | + serverNum_ = serverNum; | ||
| 24 | + CHK_PRT_RET( | ||
| 25 | + serverNum == 0 || interRankSize % serverNum != 0, | ||
| 26 | + HCCL_ERROR("[Prepare] interRankSize[%u] not divisible by serverNum[%u]", interRankSize, serverNum), | ||
| 27 | + HCCL_E_PARA); | ||
| 28 | + intraRankSize_ = interRankSize / serverNum_; | ||
| 29 | + return HCCL_SUCCESS; | ||
| 30 | +} | ||
| 31 | + | ||
| 32 | +HcclResult ReduceScatterVBIRSInter::LocalCopyPreproc(ThreadHandle& thread, const u32 rank, u64 localStrideSize) | ||
| 33 | +{ | ||
| 34 | + void* src; | ||
| 35 | + void* dst; | ||
| 36 | + u32 cnt = 0; | ||
| 37 | + // V变体: 从input按各rank的slices_[rank].offset/size搬运到scratch(步长为localStrideSize) | ||
| 38 | + for (u32 round = 0; round < intraRankSize_ / 2; round++) { | ||
| 39 | + for (u32 i = 0; i < serverNum_; i++) { | ||
| 40 | + u32 rankIdx = 2 * round + i * intraRankSize_ + rank % 2; | ||
| 41 | + if (slices_[rankIdx].size != 0) { | ||
| 42 | + src = static_cast<void*>(static_cast<u8*>(inputMem_.addr) + slices_[rankIdx].offset); | ||
| 43 | + dst = static_cast<void*>(static_cast<u8*>(scratchMem_.addr) + cnt * localStrideSize); | ||
| 44 | + CHK_RET(static_cast<HcclResult>(HcommLocalCopyOnThread(thread, dst, src, slices_[rankIdx].size))); | ||
| 45 | + } | ||
| 46 | + cnt++; | ||
| 47 | + } | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + for (u32 c = 0; c < serverNum_; c++) { | ||
| 51 | + u32 rankIdx = hccs_neighbour_rank[0] % intraRankSize_ + c * intraRankSize_; | ||
| 52 | + if (slices_[rankIdx].size == 0) { | ||
| 53 | + continue; | ||
| 54 | + } | ||
| 55 | + void* srcSlice = static_cast<void*>(static_cast<u8*>(inputMem_.addr) + slices_[rankIdx].offset); | ||
| 56 | + void* dstSlice = static_cast<void*>( | ||
| 57 | + static_cast<u8*>(scratchMem_.addr) + (intraRankSize_)*localStrideSize * serverNum_ + c * localStrideSize); | ||
| 58 | + CHK_RET(static_cast<HcclResult>(HcommLocalCopyOnThread(thread, dstSlice, srcSlice, slices_[rankIdx].size))); | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + return HCCL_SUCCESS; | ||
| 62 | +} | ||
| 63 | + | ||
| 64 | +void ReduceScatterVBIRSInter::CalcLocalStrideSize(const u32 rankSize) | ||
| 65 | +{ | ||
| 66 | + u64 maxSliceSize = 0; | ||
| 67 | + for (u32 i = 0; i < rankSize; i++) { | ||
| 68 | + maxSliceSize = std::max(maxSliceSize, slices_[i].size); | ||
| 69 | + } | ||
| 70 | + // 保持不对齐: 与均匀 BIRS inter 一致(reduce_scatter_birs_inter.cc RunAsync 中 localStrideSize = sliceSize | ||
| 71 | + // 同样未做对齐, 已验证可用)。HCCS WRITE 按字节、SIO write-reduce 按元素搬运, 长度均为 unitSize 整数倍, | ||
| 72 | + // 无额外对齐约束; 若按 HCCL_MIN_SLICE_ALIGN_910B(16KB) 对齐会成倍放大 scratch 需求, | ||
| 73 | + // 导致小消息场景 CheckScratchCapacity 误报容量错误 | ||
| 74 | + localStrideSize = maxSliceSize; | ||
| 75 | +} | ||
| 76 | + | ||
| 77 | +HcclResult ReduceScatterVBIRSInter::CheckScratchCapacity(u32 rankSize) | ||
| 78 | +{ | ||
| 79 | + u64 scratchDemand = (static_cast<u64>(rankSize) + static_cast<u64>(2) * serverNum_) * localStrideSize; | ||
| 80 | + CHK_PRT_RET( | ||
| 81 | + scratchDemand > scratchMem_.size, | ||
| 82 | + HCCL_ERROR( | ||
| 83 | + "[ReduceScatterVBIRSInter][CheckScratchCapacity]scratch demand[%llu] exceeds scratchMem size[%llu]", | ||
| 84 | + scratchDemand, scratchMem_.size), | ||
| 85 | + HCCL_E_PARA); | ||
| 86 | + return HCCL_SUCCESS; | ||
| 87 | +} | ||
| 88 | + | ||
| 89 | +HcclResult ReduceScatterVBIRSInter::Preprocess(const u32 rank, const u32 rankSize, std::vector<ChannelInfo>& channels) | ||
| 90 | +{ | ||
| 91 | + HCCL_INFO( | ||
| 92 | + "ReduceScatterVBIRSInter run: rank[%u] rankSize[%u] inputMem[%p] to outputMem[%p] count[%llu]", rank, rankSize, | ||
| 93 | + inputMem_.addr, outputMem_.addr, count_); | ||
| 94 | + return ReduceScatterVBIRS::Preprocess(rank, rankSize, channels); | ||
| 95 | +} | ||
| 96 | + | ||
| 97 | +HcclResult ReduceScatterVBIRSInter::HCCSIntraStep( | ||
| 98 | + u32 round, const u32 rank, const u32 rankSize, u32 rankSizeX_, u64 localStrideSize) | ||
| 99 | +{ | ||
| 100 | + // V变体: 按槽位精确WRITE(每槽只搬运 slices_[target].size, padding 区不参与传输), | ||
| 101 | + // 槽位 cnt 对应的目标 rank 为 hccs_ranks[round-1]%intra + cnt*intra | ||
| 102 | + if (round != 0) { | ||
| 103 | + CHK_RET(static_cast<HcclResult>( | ||
| 104 | + HcommChannelNotifyRecordOnThread(subThreads[0], hccs_links_reversed[round - 1].handle, NOTIFY_IDX_ACK))); | ||
| 105 | + CHK_RET(static_cast<HcclResult>(HcommChannelNotifyWaitOnThread( | ||
| 106 | + subThreads[0], hccs_links[round - 1].handle, NOTIFY_IDX_ACK, CUSTOM_TIMEOUT))); | ||
| 107 | + u64 localOffsetByte = hccs_ranks[round - 1] % intraRankSize_ / rankSizeX_ * localStrideSize * serverNum_; | ||
| 108 | + u64 remoteOffsetByte | ||
| 109 | + = ((intraRankSize_ / rankSizeX_) + rank % intraRankSize_ / rankSizeX_) * localStrideSize * serverNum_; | ||
| 110 | + u32 targetBase = hccs_ranks[round - 1] % intraRankSize_; | ||
| 111 | + for (u32 cnt = 0; cnt < serverNum_; cnt++) { | ||
| 112 | + u64 sliceSize = slices_[targetBase + cnt * intraRankSize_].size; | ||
| 113 | + if (sliceSize == 0) { | ||
| 114 | + continue; | ||
| 115 | + } | ||
| 116 | + void* src | ||
| 117 | + = static_cast<void*>(static_cast<u8*>(scratchMem_.addr) + localOffsetByte + cnt * localStrideSize); | ||
| 118 | + void* dst = static_cast<void*>( | ||
| 119 | + static_cast<u8*>(hccs_links[round - 1].remoteOutput.addr) + remoteOffsetByte + cnt * localStrideSize); | ||
| 120 | + | ||
| 121 | + CHK_RET(static_cast<HcclResult>( | ||
| 122 | + HcommWriteOnThread(subThreads[0], hccs_links[round - 1].handle, dst, src, sliceSize))); | ||
| 123 | + } | ||
| 124 | + | ||
| 125 | + CHK_RET(static_cast<HcclResult>( | ||
| 126 | + HcommChannelNotifyRecordOnThread(subThreads[0], hccs_links[round - 1].handle, NOTIFY_IDX_DATA_SIGNAL))); | ||
| 127 | + CHK_RET(static_cast<HcclResult>(HcommChannelNotifyWaitOnThread( | ||
| 128 | + subThreads[0], hccs_links_reversed[round - 1].handle, NOTIFY_IDX_DATA_SIGNAL, CUSTOM_TIMEOUT))); | ||
| 129 | + } | ||
| 130 | + return HCCL_SUCCESS; | ||
| 131 | +} | ||
| 132 | + | ||
| 133 | +HcclResult ReduceScatterVBIRSInter::SIOIntraStep( | ||
| 134 | + u32 round, const u32 rank, const u32 rankSize, u32 rankSizeX_, u64 localStrideSize) | ||
| 135 | +{ | ||
| 136 | + // V变体: 按槽位精确write-reduce(每槽只规约 slices_[target].size, padding 区不参与规约, | ||
| 137 | + // 避免规约未初始化数据); 槽位 cnt 对应的目标 rank 为 targetRank%intra + cnt*intra, | ||
| 138 | + // 链路两端对同一目标 rank 使用相同切片长度(sendCounts/sendDispls 各rank一致) | ||
| 139 | + CHK_RET(static_cast<HcclResult>(HcommChannelNotifyRecordOnThread(mainThread, sio_link.handle, NOTIFY_IDX_ACK))); | ||
| 140 | + CHK_RET(static_cast<HcclResult>( | ||
| 141 | + HcommChannelNotifyWaitOnThread(mainThread, sio_link.handle, NOTIFY_IDX_ACK, CUSTOM_TIMEOUT))); | ||
| 142 | + | ||
| 143 | + u64 localOffsetByte = intraRankSize_ + (round % 2); | ||
| 144 | + u64 remoteOffsetByte = (round != hccs_ranks.size()) ? ((hccs_ranks[round] % intraRankSize_) / rankSizeX_) : | ||
| 145 | + (rank % intraRankSize_ / rankSizeX_); | ||
| 146 | + localOffsetByte *= localStrideSize * serverNum_; | ||
| 147 | + remoteOffsetByte *= localStrideSize * serverNum_; | ||
| 148 | + u32 targetRank = (round != hccs_ranks.size()) ? hccs_neighbour_rank[round] : sio_rank; | ||
| 149 | + for (u32 cnt = 0; cnt < serverNum_; cnt++) { | ||
| 150 | + u64 sliceSize = slices_[targetRank % intraRankSize_ + cnt * intraRankSize_].size; | ||
| 151 | + if (sliceSize == 0) { | ||
| 152 | + continue; | ||
| 153 | + } | ||
| 154 | + void* src = static_cast<void*>(static_cast<u8*>(scratchMem_.addr) + localOffsetByte + cnt * localStrideSize); | ||
| 155 | + void* dst = static_cast<void*>( | ||
| 156 | + static_cast<u8*>(sio_link.remoteOutput.addr) + remoteOffsetByte + cnt * localStrideSize); | ||
| 157 | + | ||
| 158 | + CHK_RET(static_cast<HcclResult>(HcommWriteReduceOnThread( | ||
| 159 | + mainThread, sio_link.handle, dst, src, sliceSize / unitSize, static_cast<HcommDataType>(dataType_), | ||
| 160 | + static_cast<HcommReduceOp>(reductionOp_)))); | ||
| 161 | + } | ||
| 162 | + | ||
| 163 | + CHK_RET( | ||
| 164 | + static_cast<HcclResult>(HcommChannelNotifyRecordOnThread(mainThread, sio_link.handle, NOTIFY_IDX_DATA_SIGNAL))); | ||
| 165 | + CHK_RET(static_cast<HcclResult>( | ||
| 166 | + HcommChannelNotifyWaitOnThread(mainThread, sio_link.handle, NOTIFY_IDX_DATA_SIGNAL, CUSTOM_TIMEOUT))); | ||
| 167 | + | ||
| 168 | + return HCCL_SUCCESS; | ||
| 169 | +} | ||
| 170 | + | ||
| 171 | +HcclResult ReduceScatterVBIRSInter::LocalCopyIntraStep( | ||
| 172 | + u32 round, const u32 rank, const u32 rankSize, u32 rankSizeX_, u64 localStrideSize) | ||
| 173 | +{ | ||
| 174 | + // V变体: 从input按各rank的slices_[rank].offset/size搬运到scratch(下一轮SIO用的ping-pong区) | ||
| 175 | + if (round < hccs_ranks.size()) { | ||
| 176 | + for (u32 cnt = 0; cnt < serverNum_; cnt++) { | ||
| 177 | + u32 rankIdx = (round < hccs_ranks.size() - 1) ? | ||
| 178 | + ((hccs_neighbour_rank[round + 1]) % intraRankSize_ + cnt * intraRankSize_) : | ||
| 179 | + ((sio_rank) % intraRankSize_ + cnt * intraRankSize_); | ||
| 180 | + if (slices_[rankIdx].size == 0) { | ||
| 181 | + continue; | ||
| 182 | + } | ||
| 183 | + u64 srcOffsetByte = slices_[rankIdx].offset; | ||
| 184 | + u64 dstOffsetByte | ||
| 185 | + = (intraRankSize_ + ((round + 1) % 2)) * localStrideSize * serverNum_ + cnt * localStrideSize; | ||
| 186 | + | ||
| 187 | + void* srcSlice = static_cast<void*>(static_cast<u8*>(inputMem_.addr) + srcOffsetByte); | ||
| 188 | + void* dstSlice = static_cast<void*>(static_cast<u8*>(scratchMem_.addr) + dstOffsetByte); | ||
| 189 | + CHK_RET(static_cast<HcclResult>( | ||
| 190 | + HcommLocalCopyOnThread(subThreads[1], dstSlice, srcSlice, slices_[rankIdx].size))); | ||
| 191 | + } | ||
| 192 | + } | ||
| 193 | + | ||
| 194 | + return HCCL_SUCCESS; | ||
| 195 | +} | ||
| 196 | + | ||
| 197 | +HcclResult ReduceScatterVBIRSInter::PreprocInterServer( | ||
| 198 | + const u32 rank, const u32 rankSize, u32 rankSizeX_, u64 localStrideSize, std::vector<ChannelInfo>& channels) | ||
| 199 | +{ | ||
| 200 | + // V变体: 跨服务器前置阶段, 本rank最终接收量为 slices_[rank].size | ||
| 201 | + u64 localSliceSize = slices_[rank].size; | ||
| 202 | + vec_offsets.clear(); | ||
| 203 | + for (u32 i = 0; i < (intraRankSize_ / rankSizeX_); i++) { | ||
| 204 | + if (i == (rank % intraRankSize_ / rankSizeX_)) { | ||
| 205 | + vec_offsets.push_back((rank % intraRankSize_ / rankSizeX_) * localStrideSize * serverNum_); | ||
| 206 | + } else { | ||
| 207 | + vec_offsets.push_back(((intraRankSize_ / rankSizeX_) + i) * localStrideSize * serverNum_); | ||
| 208 | + } | ||
| 209 | + } | ||
| 210 | + | ||
| 211 | + // V变体: 树形规约按槽位精确执行(每槽只规约 slices_[target].size, padding 区不参与规约); | ||
| 212 | + // 各块槽位 cnt 对应的目标 rank 均为 rank%intra + cnt*intra, 切片长度跨块一致 | ||
| 213 | + auto ind = intraRankSize_ / rankSizeX_; | ||
| 214 | + for (u32 stride = 1; stride < ind; stride *= 2) { | ||
| 215 | + for (u32 i = stride; i < ind; i += stride * 2) { | ||
| 216 | + for (u32 cnt = 0; cnt < serverNum_; cnt++) { | ||
| 217 | + u64 sliceSize = slices_[rank % intraRankSize_ + cnt * intraRankSize_].size; | ||
| 218 | + if (sliceSize == 0) { | ||
| 219 | + continue; | ||
| 220 | + } | ||
| 221 | + CHK_RET(LocalReduceCCLToCCL( | ||
| 222 | + vec_offsets[i] + cnt * localStrideSize, vec_offsets[i - stride] + cnt * localStrideSize, sliceSize, | ||
| 223 | + mainThread)); | ||
| 224 | + } | ||
| 225 | + } | ||
| 226 | + } | ||
| 227 | + | ||
| 228 | + u64 remoteOffsetByte = localStrideSize * serverNum_; | ||
| 229 | + u64 localOffsetByte = vec_offsets[0] + localStrideSize * (((rank) % rankSize) / intraRankSize_); | ||
| 230 | + void* src = static_cast<void*>(static_cast<u8*>(scratchMem_.addr) + localOffsetByte); | ||
| 231 | + void* dst = static_cast<void*>(static_cast<u8*>(scratchMem_.addr) + remoteOffsetByte); | ||
| 232 | + CHK_RET(static_cast<HcclResult>(HcommLocalCopyOnThread(mainThread, dst, src, localSliceSize))); | ||
| 233 | + return HCCL_SUCCESS; | ||
| 234 | +} | ||
| 235 | + | ||
| 236 | +HcclResult ReduceScatterVBIRSInter::InterServer( | ||
| 237 | + const u32 rank, const u32 rankSize, u32 rankSizeX_, u64 localStrideSize, std::vector<ChannelInfo>& channels) | ||
| 238 | +{ | ||
| 239 | + // V变体: 跨服务器阶段, 本rank最终接收量为 slices_[rank].size | ||
| 240 | + u64 localSliceSize = slices_[rank].size; | ||
| 241 | + for (u32 round = 1; round < serverNum_; round++) { | ||
| 242 | + GetNotifyIdxMainToSub(notifyIdxMainToSub_); | ||
| 243 | + CHK_RET(PreSyncInterThreads(mainThread, subThreads, notifyIdxMainToSub_)); | ||
| 244 | + CHK_RET(static_cast<HcclResult>(HcommChannelNotifyRecordOnThread( | ||
| 245 | + mainThread, channels[((serverNum_ - round) * intraRankSize_ + rank) % rankSize].handle, NOTIFY_IDX_ACK))); | ||
| 246 | + CHK_RET(static_cast<HcclResult>(HcommChannelNotifyWaitOnThread( | ||
| 247 | + mainThread, channels[((round)*intraRankSize_ + rank) % rankSize].handle, NOTIFY_IDX_ACK, CUSTOM_TIMEOUT))); | ||
| 248 | + u64 remoteOffsetByte = localStrideSize * serverNum_ + localStrideSize * round; | ||
| 249 | + // V变体: 本轮向 peerRank(同intra位置、其他server)转发其目标切片的本server内规约和, | ||
| 250 | + // 传输长度必须取 slices_[peerRank].size(目标rank的切片长度)而非本rank的 localSliceSize | ||
| 251 | + u32 peerRank = ((round)*intraRankSize_ + rank) % rankSize; | ||
| 252 | + u64 peerSliceSize = slices_[peerRank].size; | ||
| 253 | + u64 localOffsetByte = vec_offsets[0] + localStrideSize * (peerRank / intraRankSize_); | ||
| 254 | + void* src = static_cast<void*>(static_cast<u8*>(scratchMem_.addr) + localOffsetByte); | ||
| 255 | + void* dst = static_cast<void*>(static_cast<u8*>(channels[peerRank].remoteOutput.addr) + remoteOffsetByte); | ||
| 256 | + if (peerSliceSize != 0) { | ||
| 257 | + CHK_RET(static_cast<HcclResult>( | ||
| 258 | + HcommWriteOnThread(mainThread, channels[peerRank].handle, dst, src, peerSliceSize))); | ||
| 259 | + } | ||
| 260 | + CHK_RET(static_cast<HcclResult>(HcommChannelNotifyRecordOnThread( | ||
| 261 | + mainThread, channels[((round)*intraRankSize_ + rank) % rankSize].handle, NOTIFY_IDX_DATA_SIGNAL))); | ||
| 262 | + CHK_RET(static_cast<HcclResult>(HcommChannelNotifyWaitOnThread( | ||
| 263 | + mainThread, channels[((serverNum_ - round) * intraRankSize_ + rank) % rankSize].handle, | ||
| 264 | + NOTIFY_IDX_DATA_SIGNAL, CUSTOM_TIMEOUT))); | ||
| 265 | + GetNotifyIdxSubToMain(notifyIdxSubToMain_); | ||
| 266 | + CHK_RET(PostSyncInterThreads(mainThread, subThreads, notifyIdxSubToMain_)); | ||
| 267 | + } | ||
| 268 | + u64 ptrSz = (serverNum_ == 1 ? vec_offsets[0] : localStrideSize * serverNum_); | ||
| 269 | + GetNotifyIdxMainToSub(notifyIdxMainToSub_); | ||
| 270 | + CHK_RET(PreSyncInterThreads(mainThread, subThreads, notifyIdxMainToSub_)); | ||
| 271 | + for (u32 step = 1; step < serverNum_; step *= 2) { | ||
| 272 | + for (u32 i = 0; i + step < serverNum_; i += 2 * step) { | ||
| 273 | + CHK_RET(LocalReduceCCLToCCL( | ||
| 274 | + ptrSz + localStrideSize * (i + step), ptrSz + localStrideSize * i, localSliceSize, mainThread)); | ||
| 275 | + } | ||
| 276 | + } | ||
| 277 | + void* srcSlice = static_cast<void*>(static_cast<u8*>(scratchMem_.addr) + ptrSz); | ||
| 278 | + void* dstSlice = static_cast<void*>(static_cast<u8*>(outputMem_.addr)); | ||
| 279 | + CHK_RET(static_cast<HcclResult>(HcommLocalCopyOnThread(mainThread, dstSlice, srcSlice, localSliceSize))); | ||
| 280 | + return HCCL_SUCCESS; | ||
| 281 | +} | ||
| 282 | + | ||
| 283 | +// scatter的入口函数 | ||
| 284 | +HcclResult ReduceScatterVBIRSInter::RunAsync(const u32 rank, const u32 rankSize, std::vector<ChannelInfo>& channels) | ||
| 285 | +{ | ||
| 286 | + hccs_ranks.clear(); | ||
| 287 | + hccs_neighbour_rank.clear(); | ||
| 288 | + hccs_links.clear(); | ||
| 289 | + | ||
| 290 | + CHK_RET(Preprocess(rank, rankSize, channels)); | ||
| 291 | + | ||
| 292 | + if (intraRankSize_ % rankSizeX_ != 0) { | ||
| 293 | + HCCL_ERROR( | ||
| 294 | + "[ReduceScatterVBIRSInter][RunAsync]intraRankSize_[%u] is not evenly divisible by rankSizeX_[%u]", | ||
| 295 | + intraRankSize_, rankSizeX_); | ||
| 296 | + return HCCL_E_INTERNAL; | ||
| 297 | + } | ||
| 298 | + | ||
| 299 | + sio_rank = rank ^ 1; | ||
| 300 | + sio_link = channels[sio_rank]; | ||
| 301 | + | ||
| 302 | + for (u32 i = 1; i < intraRankSize_ / rankSizeX_; ++i) { | ||
| 303 | + u32 current_hccs_rank | ||
| 304 | + = (rank % intraRankSize_ + rankSizeX_ * i) % (intraRankSize_) + rank / intraRankSize_ * intraRankSize_; | ||
| 305 | + hccs_ranks.push_back(current_hccs_rank); | ||
| 306 | + hccs_neighbour_rank.push_back(current_hccs_rank ^ 1); | ||
| 307 | + hccs_links.push_back(channels[hccs_ranks[i - 1]]); | ||
| 308 | + } | ||
| 309 | + hccs_links_reversed.assign(hccs_links.rbegin(), hccs_links.rend()); | ||
| 310 | + | ||
| 311 | + CalcLocalStrideSize(rankSize); | ||
| 312 | + | ||
| 313 | + CHK_RET(CheckScratchCapacity(rankSize)); | ||
| 314 | + | ||
| 315 | + // MainRecordSub + SubWaitMain | ||
| 316 | + GetNotifyIdxMainToSub(notifyIdxMainToSub_); | ||
| 317 | + CHK_RET(PreSyncInterThreads(mainThread, subThreads, notifyIdxMainToSub_)); | ||
| 318 | + | ||
| 319 | + if (!hccs_neighbour_rank.empty()) { | ||
| 320 | + CHK_RET(LocalCopyPreproc(mainThread, rank, localStrideSize)); | ||
| 321 | + } | ||
| 322 | + | ||
| 323 | + GetNotifyIdxSubToMain(notifyIdxSubToMain_); | ||
| 324 | + CHK_RET(PostSyncInterThreads(mainThread, subThreads, notifyIdxSubToMain_)); | ||
| 325 | + for (u32 round = 0; round < hccs_ranks.size() + 1; round++) { | ||
| 326 | + // MainRecordSub + SubWaitMain | ||
| 327 | + CHK_RET(PreSyncInterThreads(mainThread, subThreads, notifyIdxMainToSub_)); | ||
| 328 | + | ||
| 329 | + CHK_RET(HCCSIntraStep(round, rank, rankSize, rankSizeX_, localStrideSize)); | ||
| 330 | + CHK_RET(SIOIntraStep(round, rank, rankSize, rankSizeX_, localStrideSize)); | ||
| 331 | + CHK_RET(LocalCopyIntraStep(round, rank, rankSize, rankSizeX_, localStrideSize)); | ||
| 332 | + | ||
| 333 | + // SubRecordMain + MainWaitSub | ||
| 334 | + CHK_RET(PostSyncInterThreads(mainThread, subThreads, notifyIdxSubToMain_)); | ||
| 335 | + } | ||
| 336 | + | ||
| 337 | + // MainRecordSub + SubWaitMain | ||
| 338 | + CHK_RET(PreSyncInterThreads(mainThread, subThreads, notifyIdxMainToSub_)); | ||
| 339 | + | ||
| 340 | + CHK_RET(PreprocInterServer(rank, rankSize, rankSizeX_, localStrideSize, channels)); | ||
| 341 | + | ||
| 342 | + CHK_RET(PostSyncInterThreads(mainThread, subThreads, notifyIdxSubToMain_)); | ||
| 343 | + | ||
| 344 | + CHK_RET(InterServer(rank, rankSize, rankSizeX_, localStrideSize, channels)); | ||
| 345 | + | ||
| 346 | + CHK_RET(PostSyncInterThreads(mainThread, subThreads, notifyIdxSubToMain_)); | ||
| 347 | + HCCL_INFO("ReduceScatterVBIRSInter finished: rank[%u]", rank); | ||
| 348 | + return HCCL_SUCCESS; | ||
| 349 | +} | ||
| 350 | + | ||
| 351 | +REGISTER_TEMPLATE(TEMPLATE_REDUCE_SCATTER_V_BIRS_INTER, ReduceScatterVBIRSInter); | ||
| 352 | +} // namespace ops_hccl_experimental | ||
| @@ -0,0 +1,59 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 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 | +#ifndef REDUCE_SCATTER_V_BIRS_INTER_H | ||
| 12 | +#define REDUCE_SCATTER_V_BIRS_INTER_H | ||
| 13 | + | ||
| 14 | +#include "reduce_scatter_v_birs.h" | ||
| 15 | +#include "alg_template_base_experimental.h" | ||
| 16 | + | ||
| 17 | +namespace ops_hccl_experimental { | ||
| 18 | + | ||
| 19 | +// ReduceScatterV 的 BIRS 跨服务器(Inter-Server)算法模板 | ||
| 20 | +// 注意: scratch 以 localStrideSize*serverNum_ 为块、localStrideSize(各rank切片大小最大值, 不对齐)为槽位步长组织; | ||
| 21 | +// HCCS WRITE / SIO write-reduce / 树形 local-reduce 均按槽位以实际切片长度(slices_[target].size)精确执行, | ||
| 22 | +// padding 区不参与任何传输与规约(避免规约未初始化数据导致的不确定结果), 对各规约类型均正确。 | ||
| 23 | +// 前置条件与 ReduceScatterVBIRS 相同: 所有 rank 传入一致的 sendCounts/sendDispls 数组, | ||
| 24 | +// 保证链路两端及各块间同一槽位的切片长度一致。当各rank接收量一致(均匀)时与原BIRS数据流等价。 | ||
| 25 | +class ReduceScatterVBIRSInter : public ReduceScatterVBIRS { | ||
| 26 | +public: | ||
| 27 | + explicit ReduceScatterVBIRSInter(); | ||
| 28 | + | ||
| 29 | + ~ReduceScatterVBIRSInter() override; | ||
| 30 | + | ||
| 31 | + HcclResult Prepare(u32 serverNum, u32 interRankSize) override; | ||
| 32 | + | ||
| 33 | + HcclResult RunAsync(const u32 rank, const u32 rankSize, std::vector<ChannelInfo>& channels) override; | ||
| 34 | + | ||
| 35 | +protected: | ||
| 36 | + HcclResult LocalCopyPreproc(ThreadHandle& stream, const u32 rank, u64 localStrideSize); | ||
| 37 | + // V变体: inter 不对齐步长, 保持批量传输结构(各rank接收量一致时与原inter等价) | ||
| 38 | + void CalcLocalStrideSize(const u32 rankSize) override; | ||
| 39 | + HcclResult CheckScratchCapacity(u32 rankSize) override; | ||
| 40 | + | ||
| 41 | + HcclResult Preprocess(const u32 rank, const u32 rankSize, std::vector<ChannelInfo>& channels) override; | ||
| 42 | + HcclResult HCCSIntraStep(u32 round, const u32 rank, const u32 rankSize, u32 rankSizeX_, u64 localStrideSize); | ||
| 43 | + HcclResult SIOIntraStep(u32 round, const u32 rank, const u32 rankSize, u32 rankSizeX_, u64 localStrideSize); | ||
| 44 | + HcclResult LocalCopyIntraStep(u32 round, const u32 rank, const u32 rankSize, u32 rankSizeX_, u64 localStrideSize); | ||
| 45 | + HcclResult PreprocInterServer( | ||
| 46 | + const u32 rank, const u32 rankSize, u32 rankSizeX_, u64 localStrideSize, std::vector<ChannelInfo>& channels); | ||
| 47 | + HcclResult InterServer( | ||
| 48 | + const u32 rank, const u32 rankSize, u32 rankSizeX_, u64 localStrideSize, std::vector<ChannelInfo>& channels); | ||
| 49 | + | ||
| 50 | +private: | ||
| 51 | + u32 serverNum_; | ||
| 52 | + u32 intraRankSize_; | ||
| 53 | + u32 rankSizeX_ = 2; | ||
| 54 | + std::vector<u32> vec_offsets; | ||
| 55 | + u64 localStrideSize; | ||
| 56 | +}; | ||
| 57 | + | ||
| 58 | +} // namespace ops_hccl_experimental | ||
| 59 | +#endif /* * REDUCE_SCATTER_V_BIRS_INTER_H */ | ||
| @@ -0,0 +1,175 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 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 "reduce_scatter_v_op_experimental.h" | ||
| 12 | +#include "topo_host.h" | ||
| 13 | +#include <algorithm> | ||
| 14 | +#include <future> | ||
| 15 | +#include <map> | ||
| 16 | +#include <string> | ||
| 17 | +#include "op_common_experimental.h" | ||
| 18 | +#include "param_check.h" | ||
| 19 | +#include "reduce_scatter_v.h" | ||
| 20 | +#include "load_kernel.h" | ||
| 21 | +#include "hcomm_dlsym.h" | ||
| 22 | +#include "hcomm_host_profiling_dl.h" | ||
| 23 | + | ||
| 24 | +using namespace std; | ||
| 25 | + | ||
| 26 | +extern "C" HcclResult HcclReduceScatterVInner( | ||
| 27 | + void* sendBuf, const void* sendCounts, const void* sendDispls, void* recvBuf, uint64_t recvCount, | ||
| 28 | + HcclDataType dataType, HcclReduceOp op, HcclComm comm, aclrtStream stream); | ||
| 29 | + | ||
| 30 | +namespace ops_hccl_experimental { | ||
| 31 | +using ops_hccl::CheckCount; | ||
| 32 | +using ops_hccl::CheckDataTypeRSV; | ||
| 33 | +using ops_hccl::CheckReduceOp; | ||
| 34 | +using ops_hccl::CheckReduceScatterVInputParam; | ||
| 35 | +using ops_hccl::COMM_INDENTIFIER_MAX_LENGTH; | ||
| 36 | +using ops_hccl::DATATYPE_SIZE_TABLE; | ||
| 37 | +using ops_hccl::HcclCheckTag; | ||
| 38 | +using ops_hccl::HcomCheckUserRank; | ||
| 39 | +using ops_hccl::InitEnvConfig; | ||
| 40 | +using ops_hccl::LoadAICPUKernel; | ||
| 41 | +using ops_hccl::LogHcclExit; | ||
| 42 | +using ops_hccl::OpMode; | ||
| 43 | +using ops_hccl::PrepareReduceScatterVParam; | ||
| 44 | +using ops_hccl::ReduceScatterVEntryLog; | ||
| 45 | + | ||
| 46 | +HcclResult ReduceScatterVExperimental( | ||
| 47 | + void* sendBuf, const void* sendCounts, const void* sendDispls, void* recvBuf, uint64_t recvCount, | ||
| 48 | + HcclDataType dataType, HcclReduceOp op, HcclComm comm, aclrtStream stream) | ||
| 49 | +{ | ||
| 50 | + HcclUs startut = TIME_NOW(); | ||
| 51 | + CHK_RET(InitEnvConfig()); | ||
| 52 | + | ||
| 53 | + u32 rankSize = INVALID_VALUE_RANKSIZE; | ||
| 54 | + CHK_RET(HcclGetRankSize(comm, &rankSize)); | ||
| 55 | + if (rankSize == 1 || !MatchBIRSV()) { | ||
| 56 | + return HcclReduceScatterVInner(sendBuf, sendCounts, sendDispls, recvBuf, recvCount, dataType, op, comm, stream); | ||
| 57 | + } | ||
| 58 | + // BIRS-V 仅适用于 A3(DEV_TYPE_910_93) 且 rank 数为偶数(与 DecideReduceScatterVBirsAlg 的 vNotSelected | ||
| 59 | + // 判定一致); 不满足时同样回退老流程, 保证开启 HCCL_BIRS_ENABLE 后非目标拓扑的行为与未开启时一致 | ||
| 60 | + HcclDevType deviceType = HcclDevType::DEV_TYPE_COUNT; | ||
| 61 | + CHK_RET(HcclGetDeviceType(deviceType)); | ||
| 62 | + if (rankSize % 2 != 0 || deviceType != HcclDevType::DEV_TYPE_910_93) { | ||
| 63 | + HCCL_INFO( | ||
| 64 | + "[ReduceScatterVExperimental] BIRS-V not applicable, deviceType[%d] rankSize[%u], fallback to inner flow", | ||
| 65 | + static_cast<int>(deviceType), rankSize); | ||
| 66 | + return HcclReduceScatterVInner(sendBuf, sendCounts, sendDispls, recvBuf, recvCount, dataType, op, comm, stream); | ||
| 67 | + } | ||
| 68 | + // 校验sendCounts全部为0的情况 | ||
| 69 | + const u64* sendCountsAddr = reinterpret_cast<const u64*>(sendCounts); | ||
| 70 | + CHK_RET(CheckReduceScatterVInputParam(comm, sendBuf, recvBuf, recvCount, sendCounts, sendDispls, stream)); | ||
| 71 | + CHK_PRT_RET( | ||
| 72 | + std::all_of( | ||
| 73 | + sendCountsAddr, sendCountsAddr + rankSize, | ||
| 74 | + [](auto count) { | ||
| 75 | + return count == 0; | ||
| 76 | + }), | ||
| 77 | + HCCL_WARNING("input all %u elements in sendCounts are 0, return success", rankSize), HCCL_SUCCESS); | ||
| 78 | + u32 userRank = INVALID_VALUE_RANKID; | ||
| 79 | + CHK_RET(HcclGetRankId(comm, &userRank)); | ||
| 80 | + CHK_RET(HcomCheckUserRank(rankSize, userRank)); | ||
| 81 | + CHK_RET(CheckCount(recvCount)); | ||
| 82 | + CHK_RET(CheckDataTypeRSV(dataType)); | ||
| 83 | + CHK_RET(CheckReduceOp(dataType, op)); | ||
| 84 | + | ||
| 85 | + char commName[COMM_INDENTIFIER_MAX_LENGTH]; | ||
| 86 | + CHK_RET(HcclGetCommName(comm, commName)); | ||
| 87 | + const string tag = "ReduceScatterV_" + string(commName); | ||
| 88 | + CHK_RET(HcclCheckTag(tag.c_str())); | ||
| 89 | + | ||
| 90 | + /* 接口交互信息日志 */ | ||
| 91 | + CHK_RET(ReduceScatterVEntryLog( | ||
| 92 | + sendBuf, sendCounts, sendDispls, recvBuf, recvCount, dataType, op, stream, tag, rankSize, | ||
| 93 | + "HcclReduceScatterV")); | ||
| 94 | + | ||
| 95 | + // 申请OpParam参数结构体内存(包含变长varData区) | ||
| 96 | + u64 varMemSize = 2 * rankSize * sizeof(u64); | ||
| 97 | + void* paramMem = malloc(sizeof(OpParam) + varMemSize); | ||
| 98 | + if (!paramMem) { | ||
| 99 | + HCCL_ERROR("[ReduceScatterVExperimental] malloc OpParam failed!"); | ||
| 100 | + return HCCL_E_INTERNAL; | ||
| 101 | + } | ||
| 102 | + auto deleter = [](OpParam* p) { | ||
| 103 | + if (p) { | ||
| 104 | + p->~OpParam(); | ||
| 105 | + free(p); | ||
| 106 | + } | ||
| 107 | + }; | ||
| 108 | + OpParam* tmpParamPtr = new (paramMem) OpParam(); | ||
| 109 | + std::unique_ptr<OpParam, decltype(deleter)> paramPtr(tmpParamPtr, deleter); | ||
| 110 | + OpParam& param = *paramPtr; | ||
| 111 | + | ||
| 112 | + CHK_RET(ReduceScatterVOutPlaceCustom( | ||
| 113 | + param, sendBuf, sendDispls, sendCounts, recvBuf, recvCount, dataType, op, comm, stream, rankSize)); | ||
| 114 | + | ||
| 115 | + CHK_RET(LogHcclExit("HcclReduceScatterV", tag.c_str(), startut)); | ||
| 116 | + return HCCL_SUCCESS; | ||
| 117 | +} | ||
| 118 | + | ||
| 119 | +HcclResult ReduceScatterVOutPlaceCustom( | ||
| 120 | + OpParam& param, void* sendBuf, const void* sendDispls, const void* sendCounts, void* recvBuf, uint64_t recvCount, | ||
| 121 | + HcclDataType dataType, HcclReduceOp op, HcclComm comm, aclrtStream stream, u32 userRankSize) | ||
| 122 | +{ | ||
| 123 | + HCCL_INFO("Start to execute ReduceScatterVOutPlaceCustom"); | ||
| 124 | + char commName[COMM_INDENTIFIER_MAX_LENGTH]; | ||
| 125 | + CHK_RET(HcclGetCommName(comm, commName)); | ||
| 126 | + const std::string tag = "ReduceScatterV_" + std::string(commName); | ||
| 127 | + CHK_RET(HcclCheckTag(tag.c_str())); | ||
| 128 | + CHK_RET(PrepareReduceScatterVParam( | ||
| 129 | + sendBuf, sendDispls, sendCounts, recvBuf, recvCount, dataType, op, comm, stream, tag, OpMode::OPBASE, | ||
| 130 | + userRankSize, 2 * userRankSize * sizeof(u64), param)); | ||
| 131 | + // host(COMM_ENGINE_CPU_TS)编排路径由 KernelRunLevel0 直接读取 param.vDataDes.counts/displs(host指针); | ||
| 132 | + // 在 experimental 侧补齐 displs, 不改动公共 PrepareReduceScatterVParam 的 production 路径。 | ||
| 133 | + // AICPU_TS 路径下 PrepareReduceScatterVVarData 会用 device 指针覆写 counts/displs, 此赋值无副作用。 | ||
| 134 | + param.vDataDes.displs = const_cast<void*>(static_cast<const void*>(sendDispls)); | ||
| 135 | + | ||
| 136 | + if (IsAiCpuMode(param.deviceType, userRankSize)) { | ||
| 137 | + HCCL_DEBUG("is aicpu mode"); | ||
| 138 | + CHK_RET(LoadAICPUKernel()); | ||
| 139 | + param.engine = CommEngine::COMM_ENGINE_AICPU_TS; | ||
| 140 | + } else { | ||
| 141 | + HCCL_DEBUG("is host mode"); | ||
| 142 | + param.engine = CommEngine::COMM_ENGINE_CPU_TS; | ||
| 143 | + } | ||
| 144 | + | ||
| 145 | + uint64_t beginTime = 0; | ||
| 146 | + if (HcommIsProfilingSupported()) { | ||
| 147 | + beginTime = HcommGetProfilingSysCycleTime(); | ||
| 148 | + } | ||
| 149 | + | ||
| 150 | + CHK_RET(ProcessA3(comm, param, beginTime)); | ||
| 151 | + | ||
| 152 | + HCCL_INFO("Execute ReduceScatterVOutPlaceCustom success."); | ||
| 153 | + return HCCL_SUCCESS; | ||
| 154 | +} | ||
| 155 | + | ||
| 156 | +bool MatchBIRSV() | ||
| 157 | +{ | ||
| 158 | + const char* val = std::getenv("HCCL_BIRS_ENABLE"); | ||
| 159 | + if (val == nullptr) | ||
| 160 | + return false; | ||
| 161 | + std::string str = val; | ||
| 162 | + std::string lower = str; | ||
| 163 | + std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower); | ||
| 164 | + if (lower == "true") { | ||
| 165 | + return true; | ||
| 166 | + } | ||
| 167 | + if (lower == "false") { | ||
| 168 | + return false; | ||
| 169 | + } | ||
| 170 | + HCCL_WARNING( | ||
| 171 | + "[MatchBIRSV] HCCL_BIRS_ENABLE[%s] is invalid, expected TRUE/FALSE, fallback to disabled", str.c_str()); | ||
| 172 | + return false; | ||
| 173 | +} | ||
| 174 | + | ||
| 175 | +} // namespace ops_hccl_experimental | ||
| @@ -0,0 +1,46 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 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 | +#ifndef OPS_HCCL_SRC_OPS_REDUCE_SCATTER_V_OP_CUSTOM | ||
| 12 | +#define OPS_HCCL_SRC_OPS_REDUCE_SCATTER_V_OP_CUSTOM | ||
| 13 | + | ||
| 14 | +#include <string> | ||
| 15 | +#include <memory> | ||
| 16 | +#include "hccl.h" | ||
| 17 | + | ||
| 18 | +#include "alg_param.h" | ||
| 19 | +#include "executor_v2_base.h" | ||
| 20 | +#include "alg_type.h" | ||
| 21 | +#include "execute_selector.h" | ||
| 22 | + | ||
| 23 | +#ifdef __cplusplus | ||
| 24 | +extern "C" { | ||
| 25 | +#endif | ||
| 26 | + | ||
| 27 | +#ifdef __cplusplus | ||
| 28 | +} | ||
| 29 | +#endif | ||
| 30 | + | ||
| 31 | +namespace ops_hccl_experimental { | ||
| 32 | +using ops_hccl::OpParam; | ||
| 33 | + | ||
| 34 | +HcclResult ReduceScatterVExperimental( | ||
| 35 | + void* sendBuf, const void* sendCounts, const void* sendDispls, void* recvBuf, uint64_t recvCount, | ||
| 36 | + HcclDataType dataType, HcclReduceOp op, HcclComm comm, aclrtStream stream); | ||
| 37 | + | ||
| 38 | +HcclResult ReduceScatterVOutPlaceCustom( | ||
| 39 | + OpParam& param, void* sendBuf, const void* sendDispls, const void* sendCounts, void* recvBuf, uint64_t recvCount, | ||
| 40 | + HcclDataType dataType, HcclReduceOp op, HcclComm comm, aclrtStream stream, u32 userRankSize); | ||
| 41 | + | ||
| 42 | +bool MatchBIRSV(); | ||
| 43 | + | ||
| 44 | +} // namespace ops_hccl_experimental | ||
| 45 | + | ||
| 46 | +#endif | ||
| @@ -34,7 +34,12 @@ HcclResult HcclReduceScatterV( | |||
| 34 | CHK_RET(IsOutPlaceDevice(isOutPlace)); | 34 | CHK_RET(IsOutPlaceDevice(isOutPlace)); |
| 35 | // 非95设备转到老流程 | 35 | // 非95设备转到老流程 |
| 36 | if (!isOutPlace) { | 36 | if (!isOutPlace) { |
| 37 | +#ifdef ENABLE_EXPERIMENTAL | ||
| 38 | + return ops_hccl_experimental::ReduceScatterVExperimental( | ||
| 39 | + sendBuf, sendCounts, sendDispls, recvBuf, recvCount, dataType, op, comm, stream); | ||
| 40 | +#else | ||
| 37 | return HcclReduceScatterVInner(sendBuf, sendCounts, sendDispls, recvBuf, recvCount, dataType, op, comm, stream); | 41 | return HcclReduceScatterVInner(sendBuf, sendCounts, sendDispls, recvBuf, recvCount, dataType, op, comm, stream); |
| 42 | +#endif | ||
| 38 | } | 43 | } |
| 39 | HcclUs startut = TIME_NOW(); // 走老流程的判断时间不统计在内 | 44 | HcclUs startut = TIME_NOW(); // 走老流程的判断时间不统计在内 |
| 40 | // 入口的地方先解析环境变量,在初始化环境变量的时候需要设置为AICPU展开 | 45 | // 入口的地方先解析环境变量,在初始化环境变量的时候需要设置为AICPU展开 |
| @@ -19,6 +19,9 @@ | |||
| 19 | #include "executor_v2_base.h" | 19 | #include "executor_v2_base.h" |
| 20 | #include "alg_type.h" | 20 | #include "alg_type.h" |
| 21 | #include "execute_selector.h" | 21 | #include "execute_selector.h" |
| 22 | +#ifdef ENABLE_EXPERIMENTAL | ||
| 23 | +#include "reduce_scatter_v_op_experimental.h" | ||
| 24 | +#endif | ||
| 22 | 25 | ||
| 23 | #ifdef __cplusplus | 26 | #ifdef __cplusplus |
| 24 | extern "C" { | 27 | extern "C" { |
| @@ -20,6 +20,7 @@ set(TOP_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../..) | |||
| 20 | enable_testing() | 20 | enable_testing() |
| 21 | 21 | ||
| 22 | option(ENABLE_GCOV "Enable gcov/lcov coverage instrumentation" OFF) | 22 | option(ENABLE_GCOV "Enable gcov/lcov coverage instrumentation" OFF) |
| 23 | +option(ENABLE_EXPERIMENTAL "Register ST testcases requiring experimental libhccl (a3 BIRS/BIRS-V)" OFF) | ||
| 23 | include(${HCCL_DEV_BASE}/cmake/intf_pub_gccnative.cmake) | 24 | include(${HCCL_DEV_BASE}/cmake/intf_pub_gccnative.cmake) |
| 24 | 25 | ||
| 25 | if(NOT DEFINED ST_TASKS) | 26 | if(NOT DEFINED ST_TASKS) |
| @@ -34,6 +34,9 @@ CMAKE_ARGS="-DBUILD_OPEN_PROJECT=ON" | |||
| 34 | if [ "${ENABLE_GCOV}" == "on" ]; then | 34 | if [ "${ENABLE_GCOV}" == "on" ]; then |
| 35 | CMAKE_ARGS="${CMAKE_ARGS} -DENABLE_GCOV=ON" | 35 | CMAKE_ARGS="${CMAKE_ARGS} -DENABLE_GCOV=ON" |
| 36 | fi | 36 | fi |
| 37 | +if [ "${ENABLE_EXPERIMENTAL}" == "true" ]; then | ||
| 38 | + CMAKE_ARGS="${CMAKE_ARGS} -DENABLE_EXPERIMENTAL=ON" | ||
| 39 | +fi | ||
| 37 | if [ -n "${ST_TASKS}" ]; then | 40 | if [ -n "${ST_TASKS}" ]; then |
| 38 | CMAKE_ARGS="${CMAKE_ARGS} -DST_TASKS=${ST_TASKS}" | 41 | CMAKE_ARGS="${CMAKE_ARGS} -DST_TASKS=${ST_TASKS}" |
| 39 | fi | 42 | fi |
| @@ -129,6 +129,16 @@ endif() | |||
| 129 | if("all" IN_LIST ST_TASKS OR "reduce_scatter" IN_LIST ST_TASKS) | 129 | if("all" IN_LIST ST_TASKS OR "reduce_scatter" IN_LIST ST_TASKS) |
| 130 | add_st_test(reduce_scatter reduce_scatter_testcase.cc) | 130 | add_st_test(reduce_scatter reduce_scatter_testcase.cc) |
| 131 | endif() | 131 | endif() |
| 132 | +# a3(BIRS/BIRS-V) 用例依赖 experimental 构建的 libhccl(HCCL_BIRS_ENABLE 选路); | ||
| 133 | +# 非 experimental 构建下 HcclReduceScatter(V)Inner 在 ST 打桩为 E_NOT_SUPPORT, 故仅在 ON 时注册 | ||
| 134 | +if(ENABLE_EXPERIMENTAL) | ||
| 135 | + if("all" IN_LIST ST_TASKS OR "reduce_scatter_v_a3" IN_LIST ST_TASKS) | ||
| 136 | + add_st_test(reduce_scatter_v_a3 reduce_scatter_v_testcase_a3.cc) | ||
| 137 | + endif() | ||
| 138 | + if("all" IN_LIST ST_TASKS OR "reduce_scatter_a3" IN_LIST ST_TASKS) | ||
| 139 | + add_st_test(reduce_scatter_a3 reduce_scatter_testcase_a3.cc) | ||
| 140 | + endif() | ||
| 141 | +endif() | ||
| 132 | if("all" IN_LIST ST_TASKS OR "reduce_scatter_3level" IN_LIST ST_TASKS) | 142 | if("all" IN_LIST ST_TASKS OR "reduce_scatter_3level" IN_LIST ST_TASKS) |
| 133 | add_st_test(reduce_scatter_3level reduce_scatter_3level_testcase.cc) | 143 | add_st_test(reduce_scatter_3level reduce_scatter_3level_testcase.cc) |
| 134 | endif() | 144 | endif() |
| @@ -0,0 +1,299 @@ | |||
| 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 "gtest/gtest.h" | ||
| 12 | +#include "alg_env_config.h" | ||
| 13 | +#include "v_testcase_common.h" | ||
| 14 | + | ||
| 15 | +constexpr u32 DATATYPE_SIZE_TABLE_RSV[HCCL_DATA_TYPE_RESERVED] | ||
| 16 | + = {sizeof(int8_t), | ||
| 17 | + sizeof(int16_t), | ||
| 18 | + sizeof(int32_t), | ||
| 19 | + 2, | ||
| 20 | + sizeof(float), | ||
| 21 | + sizeof(int64_t), | ||
| 22 | + sizeof(uint64_t), | ||
| 23 | + sizeof(uint8_t), | ||
| 24 | + sizeof(uint16_t), | ||
| 25 | + sizeof(uint32_t), | ||
| 26 | + 8, | ||
| 27 | + 2, | ||
| 28 | + 16, | ||
| 29 | + 2, | ||
| 30 | + 1, | ||
| 31 | + 1, | ||
| 32 | + 1, | ||
| 33 | + 1}; | ||
| 34 | + | ||
| 35 | +class ST_REDUCE_SCATTER_V_TEST_A2A3 : public ::testing::Test { | ||
| 36 | +protected: | ||
| 37 | + void SetUp() override { ResetAlgEnvConfigInitState(); } | ||
| 38 | + void TearDown() override | ||
| 39 | + { | ||
| 40 | + unsetenv("HCCL_OP_EXPANSION_MODE"); | ||
| 41 | + unsetenv("HCCL_INDEPENDENT_OP"); | ||
| 42 | + unsetenv("HCCL_ENABLE_OPEN_AICPU"); | ||
| 43 | + } | ||
| 44 | + static void SetUpTestCase() {} | ||
| 45 | + static void TearDownTestCase() {} | ||
| 46 | +}; | ||
| 47 | + | ||
| 48 | +static HcclResult ReduceScatterVDispatchWithOp( | ||
| 49 | + HcclReduceOp reduceOp, u32 rankId, u64 totalCount, VDataDesTag vDataDes, HcclComm comm, aclrtStream stream) | ||
| 50 | +{ | ||
| 51 | + const u32 dataTypeSize = DATATYPE_SIZE_TABLE_RSV[vDataDes.dataType]; | ||
| 52 | + void* sendBuf = nullptr; | ||
| 53 | + void* recvBuf = nullptr; | ||
| 54 | + u64 recvDataCount = vDataDes.counts[rankId]; | ||
| 55 | + u64 sendBufSize = totalCount * dataTypeSize; | ||
| 56 | + u64 recvBufSize = recvDataCount * dataTypeSize; | ||
| 57 | + // 零接收 rank: 打桩 aclrtMalloc 拒绝 size=0, 分配单元素哑元缓冲; recvCount 仍按真实值 0 传入 | ||
| 58 | + if (recvBufSize == 0) { | ||
| 59 | + recvBufSize = dataTypeSize; | ||
| 60 | + } | ||
| 61 | + aclError mallocRet = aclrtMalloc(&sendBuf, sendBufSize, static_cast<aclrtMemMallocPolicy>(BUFFER_INPUT_MARK)); | ||
| 62 | + if (mallocRet != ACL_SUCCESS) { | ||
| 63 | + return HCCL_E_INTERNAL; | ||
| 64 | + } | ||
| 65 | + mallocRet = aclrtMalloc(&recvBuf, recvBufSize, static_cast<aclrtMemMallocPolicy>(BUFFER_OUTPUT_MARK)); | ||
| 66 | + if (mallocRet != ACL_SUCCESS) { | ||
| 67 | + return HCCL_E_INTERNAL; | ||
| 68 | + } | ||
| 69 | + return HcclReduceScatterV( | ||
| 70 | + sendBuf, vDataDes.counts.data(), vDataDes.displs.data(), recvBuf, recvDataCount, vDataDes.dataType, reduceOp, | ||
| 71 | + comm, stream); | ||
| 72 | +} | ||
| 73 | + | ||
| 74 | +static HcclResult | ||
| 75 | +ReduceScatterVDispatch(u32 rankId, u64 totalCount, VDataDesTag vDataDes, HcclComm comm, aclrtStream stream) | ||
| 76 | +{ | ||
| 77 | + return ReduceScatterVDispatchWithOp(HcclReduceOp::HCCL_REDUCE_SUM, rankId, totalCount, vDataDes, comm, stream); | ||
| 78 | +} | ||
| 79 | + | ||
| 80 | +static HcclResult | ||
| 81 | +ReduceScatterVVerifyWithOp(HcclReduceOp reduceOp, AllRankTaskQueues& taskQueues, u32 rankSize, VDataDesTag vDataDes) | ||
| 82 | +{ | ||
| 83 | + return CheckReduceScatterV(taskQueues, rankSize, reduceOp, vDataDes); | ||
| 84 | +} | ||
| 85 | + | ||
| 86 | +static HcclResult ReduceScatterVVerify(AllRankTaskQueues& taskQueues, u32 rankSize, VDataDesTag vDataDes) | ||
| 87 | +{ | ||
| 88 | + return ReduceScatterVVerifyWithOp(HcclReduceOp::HCCL_REDUCE_SUM, taskQueues, rankSize, vDataDes); | ||
| 89 | +} | ||
| 90 | + | ||
| 91 | +static auto MakeReduceScatterVDispatch(HcclReduceOp reduceOp) | ||
| 92 | +{ | ||
| 93 | + return [reduceOp](u32 rankId, u64 totalCount, VDataDesTag vDataDes, HcclComm comm, aclrtStream stream) { | ||
| 94 | + return ReduceScatterVDispatchWithOp(reduceOp, rankId, totalCount, vDataDes, comm, stream); | ||
| 95 | + }; | ||
| 96 | +} | ||
| 97 | + | ||
| 98 | +static auto MakeReduceScatterVVerify(HcclReduceOp reduceOp) | ||
| 99 | +{ | ||
| 100 | + return [reduceOp](AllRankTaskQueues& taskQueues, u32 rankSize, VDataDesTag vDataDes) { | ||
| 101 | + return ReduceScatterVVerifyWithOp(reduceOp, taskQueues, rankSize, vDataDes); | ||
| 102 | + }; | ||
| 103 | +} | ||
| 104 | + | ||
| 105 | +static void SetIndependentOpEnv() | ||
| 106 | +{ | ||
| 107 | + setenv("HCCL_OP_EXPANSION_MODE", "AI_CPU", 1); | ||
| 108 | + setenv("HCCL_INDEPENDENT_OP", "1", 1); | ||
| 109 | + setenv("HCCL_BIRS_ENABLE", "TRUE", 1); | ||
| 110 | +} | ||
| 111 | + | ||
| 112 | +static void RunReduceScatterVMultilevel( | ||
| 113 | + const TopoMeta& topoInfo, VDataDesTag vDataDes, HcclReduceOp reduceOp = HcclReduceOp::HCCL_REDUCE_SUM) | ||
| 114 | +{ | ||
| 115 | + RunVMultilevelTest( | ||
| 116 | + topoInfo, vDataDes, SetIndependentOpEnv, MakeReduceScatterVDispatch(reduceOp), | ||
| 117 | + MakeReduceScatterVVerify(reduceOp), HcclDevType::DEV_TYPE_910_93); | ||
| 118 | +} | ||
| 119 | + | ||
| 120 | +static VDataDesTag MakeVDataDes(const std::vector<u64>& counts, const std::vector<u64>& displs, HcclDataType dataType) | ||
| 121 | +{ | ||
| 122 | + VDataDesTag vDataDes; | ||
| 123 | + vDataDes.counts = counts; | ||
| 124 | + vDataDes.displs = displs; | ||
| 125 | + vDataDes.dataType = dataType; | ||
| 126 | + return vDataDes; | ||
| 127 | +} | ||
| 128 | + | ||
| 129 | +TEST_F(ST_REDUCE_SCATTER_V_TEST_A2A3, st_reduce_scatter_v_a3_114_test) | ||
| 130 | +{ | ||
| 131 | + TopoMeta topoMeta{{{0, 1, 2, 3}}}; | ||
| 132 | + VDataDesTag vDataDes; | ||
| 133 | + vDataDes.counts = {100, 100, 100, 100}; | ||
| 134 | + vDataDes.displs = {0, 100, 200, 300}; | ||
| 135 | + vDataDes.dataType = HcclDataType::HCCL_DATA_TYPE_INT32; | ||
| 136 | + | ||
| 137 | + RunReduceScatterVMultilevel(topoMeta, vDataDes); | ||
| 138 | +} | ||
| 139 | + | ||
| 140 | +TEST_F(ST_REDUCE_SCATTER_V_TEST_A2A3, st_reduce_scatter_v_a3_114_100_test) | ||
| 141 | +{ | ||
| 142 | + TopoMeta topoMeta{{{0, 1, 2, 3}}}; | ||
| 143 | + VDataDesTag vDataDes; | ||
| 144 | + vDataDes.counts = {100, 100, 100, 100}; | ||
| 145 | + vDataDes.displs = {0, 100, 0, 0}; | ||
| 146 | + vDataDes.dataType = HcclDataType::HCCL_DATA_TYPE_INT32; | ||
| 147 | + | ||
| 148 | + RunReduceScatterVMultilevel(topoMeta, vDataDes); | ||
| 149 | +} | ||
| 150 | + | ||
| 151 | +TEST_F(ST_REDUCE_SCATTER_V_TEST_A2A3, st_reduce_scatter_v_a3_124_test) | ||
| 152 | +{ | ||
| 153 | + TopoMeta topoMeta{{{0, 1, 2, 3}, {4, 5, 6, 7}}}; | ||
| 154 | + VDataDesTag vDataDes; | ||
| 155 | + vDataDes.counts = {200, 200, 200, 200, 200, 200, 200, 200}; | ||
| 156 | + vDataDes.displs = {0, 200, 400, 600, 800, 1000, 1200, 1400}; | ||
| 157 | + vDataDes.dataType = HcclDataType::HCCL_DATA_TYPE_INT32; | ||
| 158 | + | ||
| 159 | + RunReduceScatterVMultilevel(topoMeta, vDataDes); | ||
| 160 | +} | ||
| 161 | + | ||
| 162 | +TEST_F(ST_REDUCE_SCATTER_V_TEST_A2A3, st_reduce_scatter_v_a3_114_varlen_test) | ||
| 163 | +{ | ||
| 164 | + // 非均匀 counts: 各目标 rank 接收量不同, 覆盖单服务器变体的变长切片与 localStrideSize(max对齐)路径 | ||
| 165 | + TopoMeta topoMeta{{{0, 1, 2, 3}}}; | ||
| 166 | + VDataDesTag vDataDes; | ||
| 167 | + vDataDes.counts = {100, 200, 300, 50}; | ||
| 168 | + vDataDes.displs = {0, 100, 300, 600}; | ||
| 169 | + vDataDes.dataType = HcclDataType::HCCL_DATA_TYPE_INT32; | ||
| 170 | + | ||
| 171 | + RunReduceScatterVMultilevel(topoMeta, vDataDes); | ||
| 172 | +} | ||
| 173 | + | ||
| 174 | +TEST_F(ST_REDUCE_SCATTER_V_TEST_A2A3, st_reduce_scatter_v_a3_14_16_test) | ||
| 175 | +{ | ||
| 176 | + // 仿真模型初始化 | ||
| 177 | + TopoMeta topoMeta; | ||
| 178 | + std::vector<u32> args{1, 4, 16}; | ||
| 179 | + for (int i = 0; i < args[0]; i++) { | ||
| 180 | + SuperPodMeta superPodMeta; | ||
| 181 | + for (int j = 0; j < args[1]; j++) { | ||
| 182 | + ServerMeta serverMate; | ||
| 183 | + for (int k = 0; k < args[2]; k++) { | ||
| 184 | + serverMate.push_back((unsigned int)k); | ||
| 185 | + } | ||
| 186 | + superPodMeta.push_back(serverMate); | ||
| 187 | + } | ||
| 188 | + topoMeta.push_back(superPodMeta); | ||
| 189 | + } | ||
| 190 | + | ||
| 191 | + VDataDesTag vDataDes; | ||
| 192 | + | ||
| 193 | + auto rankSize = 0; | ||
| 194 | + for (u32 i = 0; i < topoMeta[0].size(); i++) | ||
| 195 | + rankSize += topoMeta[0][i].size(); | ||
| 196 | + int j = 0; | ||
| 197 | + for (u32 i = 0; i < rankSize; i++) { | ||
| 198 | + vDataDes.counts.push_back(200); | ||
| 199 | + vDataDes.displs.push_back(j); | ||
| 200 | + j += 200; | ||
| 201 | + } | ||
| 202 | + vDataDes.dataType = HcclDataType::HCCL_DATA_TYPE_INT32; | ||
| 203 | + | ||
| 204 | + RunReduceScatterVMultilevel(topoMeta, vDataDes); | ||
| 205 | +} | ||
| 206 | + | ||
| 207 | +TEST_F(ST_REDUCE_SCATTER_V_TEST_A2A3, st_reduce_scatter_v_a3_124_varlen_test) | ||
| 208 | +{ | ||
| 209 | + // 跨服务器 + 非均匀 counts: 覆盖 inter 变体的批量规约与步长 padding 路径 | ||
| 210 | + TopoMeta topoMeta{{{0, 1, 2, 3}, {4, 5, 6, 7}}}; | ||
| 211 | + VDataDesTag vDataDes; | ||
| 212 | + vDataDes.counts = {100, 200, 150, 250, 80, 120, 300, 160}; | ||
| 213 | + vDataDes.displs = {0, 100, 300, 450, 700, 780, 900, 1200}; | ||
| 214 | + vDataDes.dataType = HcclDataType::HCCL_DATA_TYPE_INT32; | ||
| 215 | + | ||
| 216 | + RunReduceScatterVMultilevel(topoMeta, vDataDes); | ||
| 217 | +} | ||
| 218 | + | ||
| 219 | +TEST_F(ST_REDUCE_SCATTER_V_TEST_A2A3, st_reduce_scatter_v_a3_114_fp16_sum_test) | ||
| 220 | +{ | ||
| 221 | + TopoMeta topoMeta{{{0, 1, 2, 3}}}; | ||
| 222 | + auto vDataDes = MakeVDataDes({100, 200, 300, 50}, {0, 100, 300, 600}, HcclDataType::HCCL_DATA_TYPE_FP16); | ||
| 223 | + RunReduceScatterVMultilevel(topoMeta, vDataDes, HcclReduceOp::HCCL_REDUCE_SUM); | ||
| 224 | +} | ||
| 225 | + | ||
| 226 | +TEST_F(ST_REDUCE_SCATTER_V_TEST_A2A3, st_reduce_scatter_v_a3_114_fp16_max_test) | ||
| 227 | +{ | ||
| 228 | + TopoMeta topoMeta{{{0, 1, 2, 3}}}; | ||
| 229 | + auto vDataDes = MakeVDataDes({100, 200, 300, 50}, {0, 100, 300, 600}, HcclDataType::HCCL_DATA_TYPE_FP16); | ||
| 230 | + RunReduceScatterVMultilevel(topoMeta, vDataDes, HcclReduceOp::HCCL_REDUCE_MAX); | ||
| 231 | +} | ||
| 232 | + | ||
| 233 | +TEST_F(ST_REDUCE_SCATTER_V_TEST_A2A3, st_reduce_scatter_v_a3_114_bf16_sum_test) | ||
| 234 | +{ | ||
| 235 | + TopoMeta topoMeta{{{0, 1, 2, 3}}}; | ||
| 236 | + auto vDataDes = MakeVDataDes({100, 200, 300, 50}, {0, 100, 300, 600}, HcclDataType::HCCL_DATA_TYPE_BFP16); | ||
| 237 | + RunReduceScatterVMultilevel(topoMeta, vDataDes, HcclReduceOp::HCCL_REDUCE_SUM); | ||
| 238 | +} | ||
| 239 | + | ||
| 240 | +TEST_F(ST_REDUCE_SCATTER_V_TEST_A2A3, st_reduce_scatter_v_a3_114_bf16_max_test) | ||
| 241 | +{ | ||
| 242 | + TopoMeta topoMeta{{{0, 1, 2, 3}}}; | ||
| 243 | + auto vDataDes = MakeVDataDes({100, 200, 300, 50}, {0, 100, 300, 600}, HcclDataType::HCCL_DATA_TYPE_BFP16); | ||
| 244 | + RunReduceScatterVMultilevel(topoMeta, vDataDes, HcclReduceOp::HCCL_REDUCE_MAX); | ||
| 245 | +} | ||
| 246 | + | ||
| 247 | +TEST_F(ST_REDUCE_SCATTER_V_TEST_A2A3, st_reduce_scatter_v_a3_114_fp32_sum_test) | ||
| 248 | +{ | ||
| 249 | + TopoMeta topoMeta{{{0, 1, 2, 3}}}; | ||
| 250 | + auto vDataDes = MakeVDataDes({100, 200, 300, 50}, {0, 100, 300, 600}, HcclDataType::HCCL_DATA_TYPE_FP32); | ||
| 251 | + RunReduceScatterVMultilevel(topoMeta, vDataDes, HcclReduceOp::HCCL_REDUCE_SUM); | ||
| 252 | +} | ||
| 253 | + | ||
| 254 | +TEST_F(ST_REDUCE_SCATTER_V_TEST_A2A3, st_reduce_scatter_v_a3_114_fp32_max_test) | ||
| 255 | +{ | ||
| 256 | + TopoMeta topoMeta{{{0, 1, 2, 3}}}; | ||
| 257 | + auto vDataDes = MakeVDataDes({100, 200, 300, 50}, {0, 100, 300, 600}, HcclDataType::HCCL_DATA_TYPE_FP32); | ||
| 258 | + RunReduceScatterVMultilevel(topoMeta, vDataDes, HcclReduceOp::HCCL_REDUCE_MAX); | ||
| 259 | +} | ||
| 260 | + | ||
| 261 | +TEST_F(ST_REDUCE_SCATTER_V_TEST_A2A3, st_reduce_scatter_v_a3_114_fp32_min_test) | ||
| 262 | +{ | ||
| 263 | + TopoMeta topoMeta{{{0, 1, 2, 3}}}; | ||
| 264 | + auto vDataDes = MakeVDataDes({100, 200, 300, 50}, {0, 100, 300, 600}, HcclDataType::HCCL_DATA_TYPE_FP32); | ||
| 265 | + RunReduceScatterVMultilevel(topoMeta, vDataDes, HcclReduceOp::HCCL_REDUCE_MIN); | ||
| 266 | +} | ||
| 267 | + | ||
| 268 | +TEST_F(ST_REDUCE_SCATTER_V_TEST_A2A3, st_reduce_scatter_v_a3_114_fp32_prod_test) | ||
| 269 | +{ | ||
| 270 | + TopoMeta topoMeta{{{0, 1, 2, 3}}}; | ||
| 271 | + auto vDataDes = MakeVDataDes({100, 200, 300, 50}, {0, 100, 300, 600}, HcclDataType::HCCL_DATA_TYPE_FP32); | ||
| 272 | + RunReduceScatterVMultilevel(topoMeta, vDataDes, HcclReduceOp::HCCL_REDUCE_PROD); | ||
| 273 | +} | ||
| 274 | + | ||
| 275 | +TEST_F(ST_REDUCE_SCATTER_V_TEST_A2A3, st_reduce_scatter_v_a3_124_fp32_max_varlen_test) | ||
| 276 | +{ | ||
| 277 | + // 跨服务器 + 非均匀 counts + 非 SUM 规约: 覆盖 inter 变体按槽位精确 write-reduce 的 op 透传 | ||
| 278 | + TopoMeta topoMeta{{{0, 1, 2, 3}, {4, 5, 6, 7}}}; | ||
| 279 | + auto vDataDes = MakeVDataDes( | ||
| 280 | + {100, 200, 150, 250, 80, 120, 300, 160}, {0, 100, 300, 450, 700, 780, 900, 1200}, | ||
| 281 | + HcclDataType::HCCL_DATA_TYPE_FP32); | ||
| 282 | + RunReduceScatterVMultilevel(topoMeta, vDataDes, HcclReduceOp::HCCL_REDUCE_MAX); | ||
| 283 | +} | ||
| 284 | + | ||
| 285 | +TEST_F(ST_REDUCE_SCATTER_V_TEST_A2A3, st_reduce_scatter_v_a3_114_zerocount_varlen_test) | ||
| 286 | +{ | ||
| 287 | + // 非均匀 + 零计数混合: rank0/rank2 接收量为 0, 覆盖空切片路径(零长度搬运不产生输出语义) | ||
| 288 | + TopoMeta topoMeta{{{0, 1, 2, 3}}}; | ||
| 289 | + auto vDataDes = MakeVDataDes({0, 200, 0, 50}, {0, 0, 200, 200}, HcclDataType::HCCL_DATA_TYPE_INT32); | ||
| 290 | + RunReduceScatterVMultilevel(topoMeta, vDataDes, HcclReduceOp::HCCL_REDUCE_SUM); | ||
| 291 | +} | ||
| 292 | + | ||
| 293 | +TEST_F(ST_REDUCE_SCATTER_V_TEST_A2A3, st_reduce_scatter_v_a3_114_skew_varlen_test) | ||
| 294 | +{ | ||
| 295 | + // 极端不均匀 counts(最大/最小比 4096:1): 覆盖 localStrideSize 按最大切片对齐后的大 padding 路径 | ||
| 296 | + TopoMeta topoMeta{{{0, 1, 2, 3}}}; | ||
| 297 | + auto vDataDes = MakeVDataDes({1, 4096, 2, 1}, {0, 1, 4097, 4099}, HcclDataType::HCCL_DATA_TYPE_INT32); | ||
| 298 | + RunReduceScatterVMultilevel(topoMeta, vDataDes, HcclReduceOp::HCCL_REDUCE_SUM); | ||
| 299 | +} | ||
| @@ -37,9 +37,9 @@ static inline u32 AnalyseRankSize(const TopoMeta& topoInfo) | |||
| 37 | template <typename DispatchFn, typename VerifyFn> | 37 | template <typename DispatchFn, typename VerifyFn> |
| 38 | void RunVMultilevelTest( | 38 | void RunVMultilevelTest( |
| 39 | const TopoMeta& topoInfo, VDataDesTag vDataDes, std::function<void()> extraEnvSetup, DispatchFn dispatchFn, | 39 | const TopoMeta& topoInfo, VDataDesTag vDataDes, std::function<void()> extraEnvSetup, DispatchFn dispatchFn, |
| 40 | - VerifyFn verifyFn) | 40 | + VerifyFn verifyFn, HcclDevType devType = HcclDevType::DEV_TYPE_950) |
| 41 | { | 41 | { |
| 42 | - SimWorld::Global()->Init(topoInfo, HcclDevType::DEV_TYPE_950); | 42 | + SimWorld::Global()->Init(topoInfo, devType); |
| 43 | setenv("HCCL_OP_EXPANSION_MODE", "AI_CPU", 1); | 43 | setenv("HCCL_OP_EXPANSION_MODE", "AI_CPU", 1); |
| 44 | if (extraEnvSetup) { | 44 | if (extraEnvSetup) { |
| 45 | extraEnvSetup(); | 45 | extraEnvSetup(); |
| @@ -376,3 +376,13 @@ target_link_libraries(scatter_aicpu_kernel PRIVATE | |||
| 376 | -Wl,--no-as-needed | 376 | -Wl,--no-as-needed |
| 377 | $<BUILD_INTERFACE:intf_llt_pub> | 377 | $<BUILD_INTERFACE:intf_llt_pub> |
| 378 | ) | 378 | ) |
| 379 | + | ||
| 380 | +# 实验算法(ReduceScatter/ReduceScatterV BIRS、recursive_executor 等): | ||
| 381 | +# 复用 experimental/ops/CMakeLists.txt 中 if(TARGET scatter_aicpu_kernel) 的注入逻辑 | ||
| 382 | +# (sources/includes/ENABLE_EXPERIMENTAL 定义), 与 host/device 构建保持单一来源; | ||
| 383 | +# ST 构建无 hccl 目标, 其 host 侧源码注入(if(TARGET hccl))自动跳过 | ||
| 384 | +if(NOT DEFINED ASCEND_CANN_PACKAGE_PATH OR "${ASCEND_CANN_PACKAGE_PATH}" STREQUAL "") | ||
| 385 | + set(ASCEND_CANN_PACKAGE_PATH ${ASCEND_HOME_PATH}) | ||
| 386 | +endif() | ||
| 387 | +set(ENABLE_EXPERIMENTAL ON) | ||
| 388 | +add_subdirectory(${HCCL_DEV_BASE}/experimental/ops ${CMAKE_CURRENT_BINARY_DIR}/experimental_ops) | ||
| @@ -11,6 +11,8 @@ | |||
| 11 | #include <vector> | 11 | #include <vector> |
| 12 | #include <atomic> | 12 | #include <atomic> |
| 13 | #include <iostream> | 13 | #include <iostream> |
| 14 | +#include <mutex> | ||
| 15 | +#include <set> | ||
| 14 | #include "acl/acl_rt.h" | 16 | #include "acl/acl_rt.h" |
| 15 | #include "acl/acl_base.h" | 17 | #include "acl/acl_base.h" |
| 16 | #include "hccl/hccl_types.h" | 18 | #include "hccl/hccl_types.h" |
| @@ -26,6 +28,13 @@ using namespace hccl; | |||
| 26 | using namespace ops_hccl; | 28 | using namespace ops_hccl; |
| 27 | thread_local uint32_t curr_dev_id = UINT32_MAX; | 29 | thread_local uint32_t curr_dev_id = UINT32_MAX; |
| 28 | 30 | ||
| 31 | +namespace { | ||
| 32 | +// 登记 aclrtMalloc else 分支用宿主 malloc 模拟的"设备"指针, 使 aclrtFree 能真实释放, 避免仿真长跑泄漏 | ||
| 33 | +// (ST 各 rank 在独立 std::thread 中并发调用, 需加锁) | ||
| 34 | +std::mutex g_stubMallocMutex; | ||
| 35 | +std::set<void*> g_stubMallocPtrs; | ||
| 36 | +} // namespace | ||
| 37 | + | ||
| 29 | extern "C" unsigned int HcclLaunchAicpuKernel(OpParam* param); | 38 | extern "C" unsigned int HcclLaunchAicpuKernel(OpParam* param); |
| 30 | 39 | ||
| 31 | #ifdef __cplusplus | 40 | #ifdef __cplusplus |
| @@ -58,19 +67,37 @@ aclError aclrtMallocHost(void** hostPtr, size_t size) | |||
| 58 | // 打桩实现,仿真运行需标记内存是INPUT和OUTPUT | 67 | // 打桩实现,仿真运行需标记内存是INPUT和OUTPUT |
| 59 | aclError aclrtMalloc(void** devPtr, size_t size, aclrtMemMallocPolicy policy) | 68 | aclError aclrtMalloc(void** devPtr, size_t size, aclrtMemMallocPolicy policy) |
| 60 | { | 69 | { |
| 70 | + if (devPtr == nullptr || size == 0) { | ||
| 71 | + HCCL_ERROR("[aclrtMalloc] invalid input devPtr or size"); | ||
| 72 | + return ACL_ERROR_INVALID_PARAM; | ||
| 73 | + } | ||
| 61 | u32 memType = static_cast<u32>(policy); | 74 | u32 memType = static_cast<u32>(policy); |
| 62 | HcclSim::SimNpu& simNpu = HcclSim::SimWorld::Global()->GetSimNpuByRankId(curr_dev_id); | 75 | HcclSim::SimNpu& simNpu = HcclSim::SimWorld::Global()->GetSimNpuByRankId(curr_dev_id); |
| 63 | if (memType == BUFFER_INPUT_MARK) { | 76 | if (memType == BUFFER_INPUT_MARK) { |
| 64 | *devPtr = reinterpret_cast<void*>(simNpu.AllocMemory(BufferType::INPUT, size)); | 77 | *devPtr = reinterpret_cast<void*>(simNpu.AllocMemory(BufferType::INPUT, size)); |
| 65 | } else if (memType == BUFFER_OUTPUT_MARK) { | 78 | } else if (memType == BUFFER_OUTPUT_MARK) { |
| 66 | *devPtr = reinterpret_cast<void*>(simNpu.AllocMemory(BufferType::OUTPUT, size)); | 79 | *devPtr = reinterpret_cast<void*>(simNpu.AllocMemory(BufferType::OUTPUT, size)); |
| 80 | + } else { | ||
| 81 | + *devPtr = malloc(size); | ||
| 82 | + if (*devPtr == nullptr) { | ||
| 83 | + HCCL_ERROR("[aclrtMalloc] malloc failed, size[%zu]", size); | ||
| 84 | + return ACL_ERROR_INTERNAL_ERROR; | ||
| 85 | + } | ||
| 86 | + std::lock_guard<std::mutex> lock(g_stubMallocMutex); | ||
| 87 | + g_stubMallocPtrs.insert(*devPtr); | ||
| 67 | } | 88 | } |
| 68 | return ACL_SUCCESS; | 89 | return ACL_SUCCESS; |
| 69 | } | 90 | } |
| 70 | 91 | ||
| 71 | aclError aclrtFree(void* devPtr) | 92 | aclError aclrtFree(void* devPtr) |
| 72 | { | 93 | { |
| 73 | - (void)devPtr; | 94 | + { |
| 95 | + std::lock_guard<std::mutex> lock(g_stubMallocMutex); | ||
| 96 | + if (g_stubMallocPtrs.erase(devPtr) > 0) { | ||
| 97 | + free(devPtr); | ||
| 98 | + return ACL_SUCCESS; | ||
| 99 | + } | ||
| 100 | + } | ||
| 74 | HCCL_WARNING("[%s] not support.", __func__); | 101 | HCCL_WARNING("[%s] not support.", __func__); |
| 75 | return ACL_SUCCESS; | 102 | return ACL_SUCCESS; |
| 76 | } | 103 | } |
| @@ -11,9 +11,10 @@ | |||
| 11 | add_subdirectory(common/prepare_ut_env) | 11 | add_subdirectory(common/prepare_ut_env) |
| 12 | add_subdirectory(common/alg_parse) | 12 | add_subdirectory(common/alg_parse) |
| 13 | add_subdirectory(common/topo_match) | 13 | add_subdirectory(common/topo_match) |
| 14 | -add_subdirectory(reduce_scatter_birs) | ||
| 15 | add_subdirectory(alltoall_hier) | 14 | add_subdirectory(alltoall_hier) |
| 16 | # recursive_executor 测试仅在 --experimental 构建(ENABLE_EXPERIMENTAL=ON)时编译 | 15 | # recursive_executor 测试仅在 --experimental 构建(ENABLE_EXPERIMENTAL=ON)时编译 |
| 17 | if(ENABLE_EXPERIMENTAL) | 16 | if(ENABLE_EXPERIMENTAL) |
| 18 | add_subdirectory(recursive_executor) | 17 | add_subdirectory(recursive_executor) |
| 18 | + add_subdirectory(reduce_scatter_birs) | ||
| 19 | + add_subdirectory(reduce_scatter_v_birs) | ||
| 19 | endif() | 20 | endif() |
| @@ -0,0 +1,136 @@ | |||
| 1 | +# ----------------------------------------------------------------------------------------------------------- | ||
| 2 | +# Copyright (c) 2026 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 | +set(_repo_root ${CMAKE_CURRENT_SOURCE_DIR}/../../..) | ||
| 12 | + | ||
| 13 | +set(_rsv_ut_include_dirs | ||
| 14 | + ${_repo_root}/experimental/ops/reduce_scatter_v/birs | ||
| 15 | + ${_repo_root}/experimental/ops/reduce_scatter_v/birs/template | ||
| 16 | + ${_repo_root}/experimental/ops/op_common/template | ||
| 17 | + ${_repo_root}/src/ops/op_common/inc | ||
| 18 | + ${_repo_root}/src/ops/op_common | ||
| 19 | + ${_repo_root}/src/ops/op_common/algorithm/template | ||
| 20 | + ${_repo_root}/src/ops/op_common/algorithm/template/registry | ||
| 21 | + ${_repo_root}/src/ops/op_common/algorithm/template/wrapper | ||
| 22 | + ${_repo_root}/src/ops/op_common/algorithm/executor | ||
| 23 | + ${_repo_root}/src/ops/op_common/algorithm/executor/registry | ||
| 24 | + ${_repo_root}/src/ops/op_common/algorithm/executor/channel | ||
| 25 | + ${_repo_root}/src/ops/op_common/algorithm/topo_match | ||
| 26 | + ${_repo_root}/src/ops/op_common/selector | ||
| 27 | + ${_repo_root}/src/ops/op_common/topo_info | ||
| 28 | + ${_repo_root}/src/common | ||
| 29 | + ${_repo_root}/src/common/hcomm_dlsym | ||
| 30 | + ${_repo_root}/src/common/hcomm_dlsym/ccu | ||
| 31 | +) | ||
| 32 | + | ||
| 33 | +# 前向兼容:探测 CANN 版本号,与主构建/ST 保持一致(宏逻辑与 test/ut/recursive_executor 一致)。 | ||
| 34 | +# 缺少该宏时 dlsym_common.h 中的桩类型块会启用,与 CANN 公共头重复定义导致 conflicting declaration。 | ||
| 35 | +macro(_detect_cann_version _target) | ||
| 36 | + file(GLOB _cann_arch_dirs LIST_DIRECTORIES true "${ASCEND_CANN_PACKAGE_PATH}/*-linux") | ||
| 37 | + if(_cann_arch_dirs) | ||
| 38 | + set(_cann_arch_dir "") | ||
| 39 | + foreach(_dir ${_cann_arch_dirs}) | ||
| 40 | + if(_dir MATCHES "/${CMAKE_SYSTEM_PROCESSOR}-linux$") | ||
| 41 | + set(_cann_arch_dir ${_dir}) | ||
| 42 | + break() | ||
| 43 | + endif() | ||
| 44 | + endforeach() | ||
| 45 | + if(NOT _cann_arch_dir) | ||
| 46 | + list(GET _cann_arch_dirs 0 _cann_arch_dir) | ||
| 47 | + endif() | ||
| 48 | + target_include_directories(${_target} PRIVATE | ||
| 49 | + ${_cann_arch_dir}/include | ||
| 50 | + ${_cann_arch_dir}/include/hccl | ||
| 51 | + ${_cann_arch_dir}/include/hcomm | ||
| 52 | + ${_cann_arch_dir}/include/hcomm/ccu | ||
| 53 | + ${_cann_arch_dir}/pkg_inc | ||
| 54 | + ${_cann_arch_dir}/pkg_inc/hccl | ||
| 55 | + ${_cann_arch_dir}/pkg_inc/base | ||
| 56 | + ${_cann_arch_dir}/pkg_inc/hcomm/ccu | ||
| 57 | + ) | ||
| 58 | + foreach(_arch_dir ${_cann_arch_dirs}) | ||
| 59 | + set(_ut_cann_ver_header "${_arch_dir}/include/version/cann_version.h") | ||
| 60 | + if(EXISTS "${_ut_cann_ver_header}") | ||
| 61 | + file(STRINGS "${_ut_cann_ver_header}" _ut_cann_ver_line | ||
| 62 | + REGEX "^#define[ \t]+CANN_VERSION_NUM[ \t]+") | ||
| 63 | + if(_ut_cann_ver_line) | ||
| 64 | + string(REGEX REPLACE "^#define[ \t]+CANN_VERSION_NUM[ \t]+(.+)$" "\\1" | ||
| 65 | + _UT_CANN_VERSION_EXPR "${_ut_cann_ver_line}") | ||
| 66 | + math(EXPR _UT_CANN_VERSION_NUM "${_UT_CANN_VERSION_EXPR}" OUTPUT_FORMAT DECIMAL) | ||
| 67 | + target_compile_definitions(${_target} | ||
| 68 | + PRIVATE CANN_VERSION_NUM=${_UT_CANN_VERSION_NUM}) | ||
| 69 | + message(STATUS "[UT] Detected CANN_VERSION_NUM = ${_UT_CANN_VERSION_NUM}") | ||
| 70 | + else() | ||
| 71 | + message(FATAL_ERROR "[UT] Failed to parse CANN_VERSION_NUM from ${_ut_cann_ver_header}: " | ||
| 72 | + "no '#define CANN_VERSION_NUM' line found. " | ||
| 73 | + "dlsym_common.h stub blocks would collide with CANN public headers.") | ||
| 74 | + endif() | ||
| 75 | + break() | ||
| 76 | + endif() | ||
| 77 | + endforeach() | ||
| 78 | + endif() | ||
| 79 | +endmacro() | ||
| 80 | + | ||
| 81 | +# ---------- reduce_scatter_v_birs_selector_test: 选路判定纯逻辑 ---------- | ||
| 82 | +set(ut_src_list | ||
| 83 | + ${CMAKE_CURRENT_SOURCE_DIR}/birs_v_selector_test.cc | ||
| 84 | + ${_repo_root}/experimental/ops/reduce_scatter_v/birs/reduce_scatter_v_birs_selector.cc | ||
| 85 | +) | ||
| 86 | + | ||
| 87 | +add_executable(reduce_scatter_v_birs_selector_test ${ut_src_list}) | ||
| 88 | + | ||
| 89 | +target_compile_options(reduce_scatter_v_birs_selector_test PRIVATE | ||
| 90 | + -U_FORTIFY_SOURCE | ||
| 91 | +) | ||
| 92 | + | ||
| 93 | +target_include_directories(reduce_scatter_v_birs_selector_test PRIVATE ${_rsv_ut_include_dirs}) | ||
| 94 | + | ||
| 95 | +_detect_cann_version(reduce_scatter_v_birs_selector_test) | ||
| 96 | + | ||
| 97 | +target_link_libraries(reduce_scatter_v_birs_selector_test PRIVATE | ||
| 98 | + $<BUILD_INTERFACE:intf_llt_pub> | ||
| 99 | + GTest::gtest_main | ||
| 100 | + -lpthread | ||
| 101 | +) | ||
| 102 | + | ||
| 103 | +run_llt_test(TARGET reduce_scatter_v_birs_selector_test TASK_NUM 1) | ||
| 104 | + | ||
| 105 | +# ---------- reduce_scatter_v_birs_core_test: 变长核心纯计算逻辑 ---------- | ||
| 106 | +# executor_base/template 源文件整体编译; HCOMM 原语与 AlgTemplateBase 运行时依赖由本目录桩提供, 不依赖硬件 | ||
| 107 | +set(core_src_list | ||
| 108 | + ${CMAKE_CURRENT_SOURCE_DIR}/birs_v_core_test.cc | ||
| 109 | + ${CMAKE_CURRENT_SOURCE_DIR}/log_stub.cc | ||
| 110 | + ${CMAKE_CURRENT_SOURCE_DIR}/hcomm_stub.cc | ||
| 111 | + ${CMAKE_CURRENT_SOURCE_DIR}/base_class_stub.cc | ||
| 112 | + ${_repo_root}/experimental/ops/reduce_scatter_v/birs/reduce_scatter_v_executor_base.cc | ||
| 113 | + ${_repo_root}/experimental/ops/reduce_scatter_v/birs/template/reduce_scatter_v_birs.cc | ||
| 114 | + ${_repo_root}/experimental/ops/reduce_scatter_v/birs/template/reduce_scatter_v_birs_inter.cc | ||
| 115 | + ${_repo_root}/experimental/ops/op_common/template/alg_template_base_experimental.cc | ||
| 116 | + ${_repo_root}/src/ops/op_common/algorithm/executor/executor_base.cc | ||
| 117 | + ${_repo_root}/src/ops/op_common/algorithm/template/registry/alg_template_register.cc | ||
| 118 | +) | ||
| 119 | + | ||
| 120 | +add_executable(reduce_scatter_v_birs_core_test ${core_src_list}) | ||
| 121 | + | ||
| 122 | +target_compile_options(reduce_scatter_v_birs_core_test PRIVATE | ||
| 123 | + -U_FORTIFY_SOURCE | ||
| 124 | +) | ||
| 125 | + | ||
| 126 | +target_include_directories(reduce_scatter_v_birs_core_test PRIVATE ${_rsv_ut_include_dirs}) | ||
| 127 | + | ||
| 128 | +_detect_cann_version(reduce_scatter_v_birs_core_test) | ||
| 129 | + | ||
| 130 | +target_link_libraries(reduce_scatter_v_birs_core_test PRIVATE | ||
| 131 | + $<BUILD_INTERFACE:intf_llt_pub> | ||
| 132 | + GTest::gtest_main | ||
| 133 | + -lpthread | ||
| 134 | +) | ||
| 135 | + | ||
| 136 | +run_llt_test(TARGET reduce_scatter_v_birs_core_test TASK_NUM 1) | ||
| @@ -0,0 +1,109 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 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 | +// UT 桩: AlgTemplateBase 的构造/析构/虚函数与线程同步原语。 | ||
| 12 | +// 真实实现(src/ops/op_common/algorithm/template/alg_template_base.cc)依赖 ExecTimeoutManager 与 | ||
| 13 | +// hcomm_primitives_dl 的 dlsym 运行时符号, 不适合 UT 独立编译; 本桩按真实实现的成员初始化语义等价提供, | ||
| 14 | +// 保证被测的 BIRS-V 模板成员(slices_/scratchMem_ 等)行为与生产一致。 | ||
| 15 | + | ||
| 16 | +#include "alg_template_base.h" | ||
| 17 | +#include "alg_data_trans_wrapper.h" | ||
| 18 | + | ||
| 19 | +namespace ops_hccl { | ||
| 20 | + | ||
| 21 | +AlgTemplateBase::AlgTemplateBase() | ||
| 22 | + : slices_(slicesDummy_), | ||
| 23 | + count_(0), | ||
| 24 | + dataBytes_(0), | ||
| 25 | + dataType_(HCCL_DATA_TYPE_RESERVED), | ||
| 26 | + reductionOp_(HCCL_REDUCE_RESERVED), | ||
| 27 | + root_(INVALID_VALUE_RANKID), | ||
| 28 | + baseOffset_(0), | ||
| 29 | + barrierSwitchOn_(true) | ||
| 30 | +{} | ||
| 31 | + | ||
| 32 | +AlgTemplateBase::~AlgTemplateBase() { slices_.clear(); } | ||
| 33 | + | ||
| 34 | +HcclResult AlgTemplateBase::Prepare(PrepareData& param) | ||
| 35 | +{ | ||
| 36 | + (void)param; | ||
| 37 | + return HCCL_E_PARA; | ||
| 38 | +} | ||
| 39 | + | ||
| 40 | +HcclResult AlgTemplateBase::Prepare( | ||
| 41 | + HcclMem& inputMem, HcclMem& outputMem, HcclMem& scratchMem, const u64 count, const HcclDataType dataType, | ||
| 42 | + ThreadHandle thread, const HcclReduceOp reductionOp, const u32 root, const std::vector<Slice>& slices, | ||
| 43 | + const u64 baseOffset, const bool disableDMAReduce) | ||
| 44 | +{ | ||
| 45 | + inputMem_ = inputMem; | ||
| 46 | + outputMem_ = outputMem; | ||
| 47 | + scratchMem_ = scratchMem; | ||
| 48 | + thread_ = thread; | ||
| 49 | + count_ = count; | ||
| 50 | + dataType_ = dataType; | ||
| 51 | + dataBytes_ = count * DataUnitSize(dataType); | ||
| 52 | + reductionOp_ = reductionOp; | ||
| 53 | + root_ = root; | ||
| 54 | + disableDMAReduce_ = disableDMAReduce; | ||
| 55 | + baseOffset_ = baseOffset; | ||
| 56 | + if (slices.size() > 0) { | ||
| 57 | + slices_.resize(slices.size()); | ||
| 58 | + slices_ = slices; | ||
| 59 | + } | ||
| 60 | + return HCCL_SUCCESS; | ||
| 61 | +} | ||
| 62 | + | ||
| 63 | +HcclResult AlgTemplateBase::Prepare(u32 interRank, u32 interRankSize) | ||
| 64 | +{ | ||
| 65 | + (void)interRank; | ||
| 66 | + (void)interRankSize; | ||
| 67 | + return HCCL_E_PARA; | ||
| 68 | +} | ||
| 69 | + | ||
| 70 | +HcclResult AlgTemplateBase::Prepare( | ||
| 71 | + HcclCollOpInfo* opInfo, const u32 userRank, const std::vector<u32>& ringsOrders, | ||
| 72 | + const std::vector<Slice>& userMemInputSlices) | ||
| 73 | +{ | ||
| 74 | + (void)opInfo; | ||
| 75 | + (void)userRank; | ||
| 76 | + (void)ringsOrders; | ||
| 77 | + (void)userMemInputSlices; | ||
| 78 | + return HCCL_E_PARA; | ||
| 79 | +} | ||
| 80 | + | ||
| 81 | +HcclResult AlgTemplateBase::RunAsync(const u32 rank, const u32 rankSize, std::vector<ChannelInfo>& channels) | ||
| 82 | +{ | ||
| 83 | + (void)rank; | ||
| 84 | + (void)rankSize; | ||
| 85 | + (void)channels; | ||
| 86 | + return HCCL_SUCCESS; | ||
| 87 | +} | ||
| 88 | + | ||
| 89 | +HcclResult PreSyncInterThreads( | ||
| 90 | + const ThreadHandle& mainThread, const std::vector<ThreadHandle>& subThreads, | ||
| 91 | + const std::vector<u32>& notifyIdxMainToSub) | ||
| 92 | +{ | ||
| 93 | + (void)mainThread; | ||
| 94 | + (void)subThreads; | ||
| 95 | + (void)notifyIdxMainToSub; | ||
| 96 | + return HCCL_SUCCESS; | ||
| 97 | +} | ||
| 98 | + | ||
| 99 | +HcclResult PostSyncInterThreads( | ||
| 100 | + const ThreadHandle& mainThread, const std::vector<ThreadHandle>& subThreads, | ||
| 101 | + const std::vector<u32>& notifyIdxSubToMain) | ||
| 102 | +{ | ||
| 103 | + (void)mainThread; | ||
| 104 | + (void)subThreads; | ||
| 105 | + (void)notifyIdxSubToMain; | ||
| 106 | + return HCCL_SUCCESS; | ||
| 107 | +} | ||
| 108 | + | ||
| 109 | +} // namespace ops_hccl | ||
| @@ -0,0 +1,253 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 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 <vector> | ||
| 12 | +#include <gtest/gtest.h> | ||
| 13 | +#include "reduce_scatter_v_executor_base.h" | ||
| 14 | +#include "reduce_scatter_v_birs.h" | ||
| 15 | +#include "reduce_scatter_v_birs_inter.h" | ||
| 16 | + | ||
| 17 | +namespace { | ||
| 18 | + | ||
| 19 | +using ops_hccl::Slice; | ||
| 20 | +using ops_hccl_experimental::ReduceScatterVBIRS; | ||
| 21 | +using ops_hccl_experimental::ReduceScatterVBIRSInter; | ||
| 22 | +using ops_hccl_experimental::ReduceScatterVExecutorBase; | ||
| 23 | + | ||
| 24 | +constexpr u64 UT_MIN_SLICE_ALIGN = 16384; // HCCL_MIN_SLICE_ALIGN_910B | ||
| 25 | + | ||
| 26 | +class TestableExecutor : public ReduceScatterVExecutorBase { | ||
| 27 | +public: | ||
| 28 | + using ReduceScatterVExecutorBase::PrepareDataSliceV; | ||
| 29 | +}; | ||
| 30 | + | ||
| 31 | +class TestableBirs : public ReduceScatterVBIRS { | ||
| 32 | +public: | ||
| 33 | + using ReduceScatterVBIRS::CalcLocalStrideSize; | ||
| 34 | + using ReduceScatterVBIRS::CheckScratchCapacity; | ||
| 35 | + | ||
| 36 | + void SetSliceSizes(const std::vector<u64>& sizes) | ||
| 37 | + { | ||
| 38 | + slicesDummy_.resize(sizes.size()); | ||
| 39 | + u64 offset = 0; | ||
| 40 | + for (size_t i = 0; i < sizes.size(); i++) { | ||
| 41 | + slicesDummy_[i].offset = offset; | ||
| 42 | + slicesDummy_[i].size = sizes[i]; | ||
| 43 | + offset += sizes[i]; | ||
| 44 | + } | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + void SetScratch(u64 size) { scratchMem_ = HcclMem{HCCL_MEM_TYPE_DEVICE, nullptr, size}; } | ||
| 48 | + | ||
| 49 | + u64 GetStride() const { return localStrideSize; } | ||
| 50 | +}; | ||
| 51 | + | ||
| 52 | +class TestableBirsInter : public ReduceScatterVBIRSInter { | ||
| 53 | +public: | ||
| 54 | + using ReduceScatterVBIRSInter::CalcLocalStrideSize; | ||
| 55 | + using ReduceScatterVBIRSInter::CheckScratchCapacity; | ||
| 56 | + | ||
| 57 | + void SetSliceSizes(const std::vector<u64>& sizes) | ||
| 58 | + { | ||
| 59 | + slicesDummy_.resize(sizes.size()); | ||
| 60 | + u64 offset = 0; | ||
| 61 | + for (size_t i = 0; i < sizes.size(); i++) { | ||
| 62 | + slicesDummy_[i].offset = offset; | ||
| 63 | + slicesDummy_[i].size = sizes[i]; | ||
| 64 | + offset += sizes[i]; | ||
| 65 | + } | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + void SetScratch(u64 size) { scratchMem_ = HcclMem{HCCL_MEM_TYPE_DEVICE, nullptr, size}; } | ||
| 69 | +}; | ||
| 70 | + | ||
| 71 | +Slice MakeSlice(u64 offset, u64 size) | ||
| 72 | +{ | ||
| 73 | + Slice slice; | ||
| 74 | + slice.offset = offset; | ||
| 75 | + slice.size = size; | ||
| 76 | + return slice; | ||
| 77 | +} | ||
| 78 | + | ||
| 79 | +// ---------- PrepareDataSliceV: sendCounts/sendDispls -> slices 的 offset/size 换算 ---------- | ||
| 80 | + | ||
| 81 | +TEST(ReduceScatterVBirsCore, PrepareDataSliceVNonUniformCounts) | ||
| 82 | +{ | ||
| 83 | + TestableExecutor executor; | ||
| 84 | + const u64 sendCounts[] = {100, 200, 300, 50}; | ||
| 85 | + const u64 sendDispls[] = {0, 100, 300, 600}; | ||
| 86 | + std::vector<Slice> dataSlice; | ||
| 87 | + ASSERT_EQ(executor.PrepareDataSliceV(sendCounts, sendDispls, 4, 4, dataSlice), HCCL_SUCCESS); | ||
| 88 | + ASSERT_EQ(dataSlice.size(), 4U); | ||
| 89 | + const Slice expected[] = {MakeSlice(0, 400), MakeSlice(400, 800), MakeSlice(1200, 1200), MakeSlice(2400, 200)}; | ||
| 90 | + for (u32 i = 0; i < 4; i++) { | ||
| 91 | + EXPECT_EQ(dataSlice[i].offset, expected[i].offset) << "slice " << i; | ||
| 92 | + EXPECT_EQ(dataSlice[i].size, expected[i].size) << "slice " << i; | ||
| 93 | + } | ||
| 94 | +} | ||
| 95 | + | ||
| 96 | +TEST(ReduceScatterVBirsCore, PrepareDataSliceVZeroCountRanks) | ||
| 97 | +{ | ||
| 98 | + TestableExecutor executor; | ||
| 99 | + const u64 sendCounts[] = {0, 200, 0, 50}; | ||
| 100 | + const u64 sendDispls[] = {0, 0, 200, 200}; | ||
| 101 | + std::vector<Slice> dataSlice; | ||
| 102 | + ASSERT_EQ(executor.PrepareDataSliceV(sendCounts, sendDispls, 4, 4, dataSlice), HCCL_SUCCESS); | ||
| 103 | + ASSERT_EQ(dataSlice.size(), 4U); | ||
| 104 | + EXPECT_EQ(dataSlice[0].size, 0U); | ||
| 105 | + EXPECT_EQ(dataSlice[1].offset, 0U); | ||
| 106 | + EXPECT_EQ(dataSlice[1].size, 800U); | ||
| 107 | + EXPECT_EQ(dataSlice[2].size, 0U); | ||
| 108 | + EXPECT_EQ(dataSlice[3].offset, 800U); | ||
| 109 | + EXPECT_EQ(dataSlice[3].size, 200U); | ||
| 110 | +} | ||
| 111 | + | ||
| 112 | +TEST(ReduceScatterVBirsCore, PrepareDataSliceVSliceNumZeroRejected) | ||
| 113 | +{ | ||
| 114 | + TestableExecutor executor; | ||
| 115 | + const u64 sendCounts[] = {100}; | ||
| 116 | + const u64 sendDispls[] = {0}; | ||
| 117 | + std::vector<Slice> dataSlice; | ||
| 118 | + EXPECT_EQ(executor.PrepareDataSliceV(sendCounts, sendDispls, 4, 0, dataSlice), HCCL_E_PARA); | ||
| 119 | +} | ||
| 120 | + | ||
| 121 | +TEST(ReduceScatterVBirsCore, PrepareDataSliceVNullPointerRejected) | ||
| 122 | +{ | ||
| 123 | + TestableExecutor executor; | ||
| 124 | + const u64 sendCounts[] = {100}; | ||
| 125 | + const u64 sendDispls[] = {0}; | ||
| 126 | + std::vector<Slice> dataSlice; | ||
| 127 | + EXPECT_NE(executor.PrepareDataSliceV(nullptr, sendDispls, 4, 1, dataSlice), HCCL_SUCCESS); | ||
| 128 | + EXPECT_NE(executor.PrepareDataSliceV(sendCounts, nullptr, 4, 1, dataSlice), HCCL_SUCCESS); | ||
| 129 | +} | ||
| 130 | + | ||
| 131 | +TEST(ReduceScatterVBirsCore, PrepareDataSliceV64BitNoTruncation) | ||
| 132 | +{ | ||
| 133 | + TestableExecutor executor; | ||
| 134 | + const u64 sendCounts[] = {0x100000000ULL, 1}; | ||
| 135 | + const u64 sendDispls[] = {0, 0x100000000ULL}; | ||
| 136 | + std::vector<Slice> dataSlice; | ||
| 137 | + ASSERT_EQ(executor.PrepareDataSliceV(sendCounts, sendDispls, 8, 2, dataSlice), HCCL_SUCCESS); | ||
| 138 | + EXPECT_EQ(dataSlice[0].size, 0x800000000ULL); | ||
| 139 | + EXPECT_EQ(dataSlice[1].offset, 0x800000000ULL); | ||
| 140 | +} | ||
| 141 | + | ||
| 142 | +TEST(ReduceScatterVBirsCore, PrepareDataSliceVResizesExistingVector) | ||
| 143 | +{ | ||
| 144 | + TestableExecutor executor; | ||
| 145 | + const u64 sendCounts[] = {10, 20}; | ||
| 146 | + const u64 sendDispls[] = {0, 10}; | ||
| 147 | + std::vector<Slice> dataSlice(7, MakeSlice(123, 456)); | ||
| 148 | + ASSERT_EQ(executor.PrepareDataSliceV(sendCounts, sendDispls, 2, 2, dataSlice), HCCL_SUCCESS); | ||
| 149 | + ASSERT_EQ(dataSlice.size(), 2U); | ||
| 150 | + EXPECT_EQ(dataSlice[0].size, 20U); | ||
| 151 | + EXPECT_EQ(dataSlice[1].offset, 20U); | ||
| 152 | +} | ||
| 153 | + | ||
| 154 | +// ---------- Intra: CalcLocalStrideSize(RoundUp 16KB) 与 CheckScratchCapacity ---------- | ||
| 155 | + | ||
| 156 | +TEST(ReduceScatterVBirsCore, IntraStrideRoundsUpMaxSliceTo16K) | ||
| 157 | +{ | ||
| 158 | + TestableBirs birs; | ||
| 159 | + birs.SetSliceSizes({1000, 300, 16385, 7}); | ||
| 160 | + birs.CalcLocalStrideSize(4); | ||
| 161 | + EXPECT_EQ(birs.GetStride(), 2 * UT_MIN_SLICE_ALIGN); | ||
| 162 | +} | ||
| 163 | + | ||
| 164 | +TEST(ReduceScatterVBirsCore, IntraStrideExactMultipleUnchanged) | ||
| 165 | +{ | ||
| 166 | + TestableBirs birs; | ||
| 167 | + birs.SetSliceSizes({100, UT_MIN_SLICE_ALIGN, 512}); | ||
| 168 | + birs.CalcLocalStrideSize(3); | ||
| 169 | + EXPECT_EQ(birs.GetStride(), UT_MIN_SLICE_ALIGN); | ||
| 170 | +} | ||
| 171 | + | ||
| 172 | +TEST(ReduceScatterVBirsCore, IntraStrideMaxAtLastRank) | ||
| 173 | +{ | ||
| 174 | + TestableBirs birs; | ||
| 175 | + birs.SetSliceSizes({100, 200, 300, 5000}); | ||
| 176 | + birs.CalcLocalStrideSize(4); | ||
| 177 | + EXPECT_EQ(birs.GetStride(), UT_MIN_SLICE_ALIGN); | ||
| 178 | +} | ||
| 179 | + | ||
| 180 | +TEST(ReduceScatterVBirsCore, IntraStrideAllZeroSlices) | ||
| 181 | +{ | ||
| 182 | + // RoundUpWithDivisor(0, d) 按约定返回 d: 全零切片(生产流程被入口 all-zero 拦截, 不会到达模板) | ||
| 183 | + // 的兜底步长为一个对齐单位, 而非 0 | ||
| 184 | + TestableBirs birs; | ||
| 185 | + birs.SetSliceSizes({0, 0, 0, 0}); | ||
| 186 | + birs.CalcLocalStrideSize(4); | ||
| 187 | + EXPECT_EQ(birs.GetStride(), UT_MIN_SLICE_ALIGN); | ||
| 188 | +} | ||
| 189 | + | ||
| 190 | +TEST(ReduceScatterVBirsCore, IntraScratchCapacityBoundary) | ||
| 191 | +{ | ||
| 192 | + TestableBirs birs; | ||
| 193 | + birs.SetSliceSizes({5000, 100, 200, 300}); | ||
| 194 | + birs.CalcLocalStrideSize(4); | ||
| 195 | + ASSERT_EQ(birs.GetStride(), UT_MIN_SLICE_ALIGN); | ||
| 196 | + birs.SetScratch(4 * UT_MIN_SLICE_ALIGN); | ||
| 197 | + EXPECT_EQ(birs.CheckScratchCapacity(4), HCCL_SUCCESS); | ||
| 198 | + birs.SetScratch(4 * UT_MIN_SLICE_ALIGN - 1); | ||
| 199 | + EXPECT_EQ(birs.CheckScratchCapacity(4), HCCL_E_PARA); | ||
| 200 | +} | ||
| 201 | + | ||
| 202 | +TEST(ReduceScatterVBirsCore, IntraScratchCapacityZeroSlicesFallbackStride) | ||
| 203 | +{ | ||
| 204 | + TestableBirs birs; | ||
| 205 | + birs.SetSliceSizes({0, 0, 0, 0}); | ||
| 206 | + birs.CalcLocalStrideSize(4); | ||
| 207 | + birs.SetScratch(4 * UT_MIN_SLICE_ALIGN); | ||
| 208 | + EXPECT_EQ(birs.CheckScratchCapacity(4), HCCL_SUCCESS); | ||
| 209 | + birs.SetScratch(4 * UT_MIN_SLICE_ALIGN - 1); | ||
| 210 | + EXPECT_EQ(birs.CheckScratchCapacity(4), HCCL_E_PARA); | ||
| 211 | +} | ||
| 212 | + | ||
| 213 | +// ---------- Inter: Prepare 校验 / 不对齐步长 / (rankSize + 2*serverNum) 容量公式 ---------- | ||
| 214 | + | ||
| 215 | +TEST(ReduceScatterVBirsCore, InterPrepareDivisibility) | ||
| 216 | +{ | ||
| 217 | + TestableBirsInter birsInter; | ||
| 218 | + EXPECT_EQ(birsInter.Prepare(2, 8), HCCL_SUCCESS); | ||
| 219 | + EXPECT_EQ(birsInter.Prepare(3, 8), HCCL_E_PARA); | ||
| 220 | +} | ||
| 221 | + | ||
| 222 | +TEST(ReduceScatterVBirsCore, InterPrepareZeroServerNumRejected) | ||
| 223 | +{ | ||
| 224 | + TestableBirsInter birsInter; | ||
| 225 | + EXPECT_EQ(birsInter.Prepare(0, 8), HCCL_E_PARA); | ||
| 226 | +} | ||
| 227 | + | ||
| 228 | +TEST(ReduceScatterVBirsCore, InterStrideNotAlignedToMinSlice) | ||
| 229 | +{ | ||
| 230 | + TestableBirsInter birsInter; | ||
| 231 | + ASSERT_EQ(birsInter.Prepare(2, 8), HCCL_SUCCESS); | ||
| 232 | + birsInter.SetSliceSizes({1000, 300, 999, 7, 500, 100, 200, 300}); | ||
| 233 | + birsInter.CalcLocalStrideSize(8); | ||
| 234 | + // demand = (rankSize + 2*serverNum) * stride = (8 + 4) * 1000; | ||
| 235 | + // 边界命中 12000 即证明 stride 未按 16KB 对齐(否则 demand 为 12*16384) | ||
| 236 | + birsInter.SetScratch(12 * 1000); | ||
| 237 | + EXPECT_EQ(birsInter.CheckScratchCapacity(8), HCCL_SUCCESS); | ||
| 238 | + birsInter.SetScratch(12 * 1000 - 1); | ||
| 239 | + EXPECT_EQ(birsInter.CheckScratchCapacity(8), HCCL_E_PARA); | ||
| 240 | +} | ||
| 241 | + | ||
| 242 | +TEST(ReduceScatterVBirsCore, InterZeroSlicesDemandZero) | ||
| 243 | +{ | ||
| 244 | + TestableBirsInter birsInter; | ||
| 245 | + ASSERT_EQ(birsInter.Prepare(4, 16), HCCL_SUCCESS); | ||
| 246 | + std::vector<u64> zeroSizes(16, 0); | ||
| 247 | + birsInter.SetSliceSizes(zeroSizes); | ||
| 248 | + birsInter.CalcLocalStrideSize(16); | ||
| 249 | + birsInter.SetScratch(0); | ||
| 250 | + EXPECT_EQ(birsInter.CheckScratchCapacity(16), HCCL_SUCCESS); | ||
| 251 | +} | ||
| 252 | + | ||
| 253 | +} // namespace | ||
| @@ -0,0 +1,119 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 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 | +#include <string> | ||
| 11 | +#include <gtest/gtest.h> | ||
| 12 | +#include "reduce_scatter_v_birs_selector.h" | ||
| 13 | + | ||
| 14 | +using ops_hccl::TopoInfo; | ||
| 15 | +using ops_hccl_experimental::BirsVSelectResult; | ||
| 16 | +using ops_hccl_experimental::BirsVSelectResultToCode; | ||
| 17 | +using ops_hccl_experimental::DecideReduceScatterVBirsAlg; | ||
| 18 | + | ||
| 19 | +namespace { | ||
| 20 | +TopoInfo MakeTopo(u32 userRankSize, u32 serverNum, HcclDevType deviceType) | ||
| 21 | +{ | ||
| 22 | + TopoInfo topo{}; | ||
| 23 | + topo.userRankSize = userRankSize; | ||
| 24 | + topo.serverNum = serverNum; | ||
| 25 | + topo.deviceType = deviceType; | ||
| 26 | + return topo; | ||
| 27 | +} | ||
| 28 | + | ||
| 29 | +const std::string BIRS_V_ALG_NAME = "ReduceScatterVBIRSExecutor"; | ||
| 30 | +} // namespace | ||
| 31 | + | ||
| 32 | +TEST(ReduceScatterVBirsSelector, ServerNumZeroIsRejectedInsteadOfDivByZero) | ||
| 33 | +{ | ||
| 34 | + std::string algName; | ||
| 35 | + TopoInfo topo = MakeTopo(8, 0, HcclDevType::DEV_TYPE_910_93); | ||
| 36 | + EXPECT_EQ(DecideReduceScatterVBirsAlg(topo, algName), BirsVSelectResult::vRejectServerNumZero); | ||
| 37 | + EXPECT_TRUE(algName.empty()); | ||
| 38 | +} | ||
| 39 | + | ||
| 40 | +TEST(ReduceScatterVBirsSelector, DualCardSingleServerIsRejected) | ||
| 41 | +{ | ||
| 42 | + std::string algName; | ||
| 43 | + TopoInfo topo = MakeTopo(2, 1, HcclDevType::DEV_TYPE_910_93); | ||
| 44 | + EXPECT_EQ(DecideReduceScatterVBirsAlg(topo, algName), BirsVSelectResult::vRejectRanksPerServerLT4); | ||
| 45 | + EXPECT_TRUE(algName.empty()); | ||
| 46 | +} | ||
| 47 | + | ||
| 48 | +TEST(ReduceScatterVBirsSelector, ThreeRanksPerServerIsRejected) | ||
| 49 | +{ | ||
| 50 | + std::string algName; | ||
| 51 | + TopoInfo topo = MakeTopo(6, 2, HcclDevType::DEV_TYPE_910_93); | ||
| 52 | + EXPECT_EQ(DecideReduceScatterVBirsAlg(topo, algName), BirsVSelectResult::vRejectRanksPerServerLT4); | ||
| 53 | + EXPECT_TRUE(algName.empty()); | ||
| 54 | +} | ||
| 55 | + | ||
| 56 | +TEST(ReduceScatterVBirsSelector, RankSizeOneIsRejected) | ||
| 57 | +{ | ||
| 58 | + std::string algName; | ||
| 59 | + TopoInfo topo = MakeTopo(1, 1, HcclDevType::DEV_TYPE_910_93); | ||
| 60 | + EXPECT_EQ(DecideReduceScatterVBirsAlg(topo, algName), BirsVSelectResult::vRejectRankSizeOne); | ||
| 61 | + EXPECT_TRUE(algName.empty()); | ||
| 62 | +} | ||
| 63 | + | ||
| 64 | +TEST(ReduceScatterVBirsSelector, FourRanksPerServerIsSelected) | ||
| 65 | +{ | ||
| 66 | + std::string algName; | ||
| 67 | + TopoInfo topo = MakeTopo(8, 2, HcclDevType::DEV_TYPE_910_93); | ||
| 68 | + EXPECT_EQ(DecideReduceScatterVBirsAlg(topo, algName), BirsVSelectResult::vSelected); | ||
| 69 | + EXPECT_EQ(algName, BIRS_V_ALG_NAME); | ||
| 70 | +} | ||
| 71 | + | ||
| 72 | +TEST(ReduceScatterVBirsSelector, ExactlyFourRanksPerServerBoundarySelected) | ||
| 73 | +{ | ||
| 74 | + std::string algName; | ||
| 75 | + TopoInfo topo = MakeTopo(4, 1, HcclDevType::DEV_TYPE_910_93); | ||
| 76 | + EXPECT_EQ(DecideReduceScatterVBirsAlg(topo, algName), BirsVSelectResult::vSelected); | ||
| 77 | + EXPECT_EQ(algName, BIRS_V_ALG_NAME); | ||
| 78 | +} | ||
| 79 | + | ||
| 80 | +TEST(ReduceScatterVBirsSelector, MoreThanFourRanksSingleServerIsSelected) | ||
| 81 | +{ | ||
| 82 | + std::string algName; | ||
| 83 | + TopoInfo topo = MakeTopo(16, 1, HcclDevType::DEV_TYPE_910_93); | ||
| 84 | + EXPECT_EQ(DecideReduceScatterVBirsAlg(topo, algName), BirsVSelectResult::vSelected); | ||
| 85 | + EXPECT_EQ(algName, BIRS_V_ALG_NAME); | ||
| 86 | +} | ||
| 87 | + | ||
| 88 | +TEST(ReduceScatterVBirsSelector, MoreThanFourRanksPerServerIsSelected) | ||
| 89 | +{ | ||
| 90 | + std::string algName; | ||
| 91 | + TopoInfo topo = MakeTopo(16, 4, HcclDevType::DEV_TYPE_910_93); | ||
| 92 | + EXPECT_EQ(DecideReduceScatterVBirsAlg(topo, algName), BirsVSelectResult::vSelected); | ||
| 93 | + EXPECT_EQ(algName, BIRS_V_ALG_NAME); | ||
| 94 | +} | ||
| 95 | + | ||
| 96 | +TEST(ReduceScatterVBirsSelector, NonA3DeviceIsNotSelected) | ||
| 97 | +{ | ||
| 98 | + std::string algName; | ||
| 99 | + TopoInfo topo = MakeTopo(8, 2, HcclDevType::DEV_TYPE_910B); | ||
| 100 | + EXPECT_EQ(DecideReduceScatterVBirsAlg(topo, algName), BirsVSelectResult::vNotSelected); | ||
| 101 | + EXPECT_TRUE(algName.empty()); | ||
| 102 | +} | ||
| 103 | + | ||
| 104 | +TEST(ReduceScatterVBirsSelector, OddRankSizeIsNotSelected) | ||
| 105 | +{ | ||
| 106 | + std::string algName; | ||
| 107 | + TopoInfo topo = MakeTopo(9, 1, HcclDevType::DEV_TYPE_910_93); | ||
| 108 | + EXPECT_EQ(DecideReduceScatterVBirsAlg(topo, algName), BirsVSelectResult::vNotSelected); | ||
| 109 | + EXPECT_TRUE(algName.empty()); | ||
| 110 | +} | ||
| 111 | + | ||
| 112 | +TEST(ReduceScatterVBirsSelector, SelectResultToCodeMapsRejectsToErrorCodes) | ||
| 113 | +{ | ||
| 114 | + EXPECT_EQ(BirsVSelectResultToCode(BirsVSelectResult::vSelected), HCCL_SUCCESS); | ||
| 115 | + EXPECT_EQ(BirsVSelectResultToCode(BirsVSelectResult::vNotSelected), HCCL_SUCCESS); | ||
| 116 | + EXPECT_EQ(BirsVSelectResultToCode(BirsVSelectResult::vRejectRankSizeOne), HCCL_E_INTERNAL); | ||
| 117 | + EXPECT_EQ(BirsVSelectResultToCode(BirsVSelectResult::vRejectServerNumZero), HCCL_E_PARA); | ||
| 118 | + EXPECT_EQ(BirsVSelectResultToCode(BirsVSelectResult::vRejectRanksPerServerLT4), HCCL_E_PARA); | ||
| 119 | +} | ||
| @@ -0,0 +1,105 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 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 | +// BIRS-V 核心逻辑 UT 的 HCOMM 打桩: 被测函数(PrepareDataSliceV/CalcLocalStrideSize/CheckScratchCapacity) | ||
| 12 | +// 为纯计算逻辑, 不触发数据搬运; 模板/executor 源文件整体编译所需的 Hcomm 符号在此提供空实现。 | ||
| 13 | + | ||
| 14 | +#include "hcomm_primitives.h" | ||
| 15 | + | ||
| 16 | +extern "C" { | ||
| 17 | + | ||
| 18 | +int32_t HcommLocalCopyOnThread(ThreadHandle thread, void* dst, const void* src, uint64_t len) | ||
| 19 | +{ | ||
| 20 | + (void)thread; | ||
| 21 | + (void)dst; | ||
| 22 | + (void)src; | ||
| 23 | + (void)len; | ||
| 24 | + return 0; | ||
| 25 | +} | ||
| 26 | + | ||
| 27 | +int32_t HcommLocalReduceOnThread( | ||
| 28 | + ThreadHandle thread, void* dst, const void* src, uint64_t count, HcommDataType dataType, HcommReduceOp reduceOp) | ||
| 29 | +{ | ||
| 30 | + (void)thread; | ||
| 31 | + (void)dst; | ||
| 32 | + (void)src; | ||
| 33 | + (void)count; | ||
| 34 | + (void)dataType; | ||
| 35 | + (void)reduceOp; | ||
| 36 | + return 0; | ||
| 37 | +} | ||
| 38 | + | ||
| 39 | +int32_t HcommWriteOnThread(ThreadHandle thread, ChannelHandle channel, void* dst, const void* src, uint64_t len) | ||
| 40 | +{ | ||
| 41 | + (void)thread; | ||
| 42 | + (void)channel; | ||
| 43 | + (void)dst; | ||
| 44 | + (void)src; | ||
| 45 | + (void)len; | ||
| 46 | + return 0; | ||
| 47 | +} | ||
| 48 | + | ||
| 49 | +int32_t HcommWriteReduceOnThread( | ||
| 50 | + ThreadHandle thread, ChannelHandle channel, void* dst, const void* src, uint64_t count, HcommDataType dataType, | ||
| 51 | + HcommReduceOp reduceOp) | ||
| 52 | +{ | ||
| 53 | + (void)thread; | ||
| 54 | + (void)channel; | ||
| 55 | + (void)dst; | ||
| 56 | + (void)src; | ||
| 57 | + (void)count; | ||
| 58 | + (void)dataType; | ||
| 59 | + (void)reduceOp; | ||
| 60 | + return 0; | ||
| 61 | +} | ||
| 62 | + | ||
| 63 | +int32_t HcommChannelNotifyRecordOnThread(ThreadHandle thread, ChannelHandle channel, uint32_t remoteNotifyIdx) | ||
| 64 | +{ | ||
| 65 | + (void)thread; | ||
| 66 | + (void)channel; | ||
| 67 | + (void)remoteNotifyIdx; | ||
| 68 | + return 0; | ||
| 69 | +} | ||
| 70 | + | ||
| 71 | +int32_t | ||
| 72 | +HcommChannelNotifyWaitOnThread(ThreadHandle thread, ChannelHandle channel, uint32_t localNotifyIdx, uint32_t timeOut) | ||
| 73 | +{ | ||
| 74 | + (void)thread; | ||
| 75 | + (void)channel; | ||
| 76 | + (void)localNotifyIdx; | ||
| 77 | + (void)timeOut; | ||
| 78 | + return 0; | ||
| 79 | +} | ||
| 80 | + | ||
| 81 | +int32_t HcommAcquireComm(const char* commId) | ||
| 82 | +{ | ||
| 83 | + (void)commId; | ||
| 84 | + return 0; | ||
| 85 | +} | ||
| 86 | + | ||
| 87 | +int32_t HcommReleaseComm(const char* commId) | ||
| 88 | +{ | ||
| 89 | + (void)commId; | ||
| 90 | + return 0; | ||
| 91 | +} | ||
| 92 | + | ||
| 93 | +int32_t HcommBatchModeStart(const char* batchTag) | ||
| 94 | +{ | ||
| 95 | + (void)batchTag; | ||
| 96 | + return 0; | ||
| 97 | +} | ||
| 98 | + | ||
| 99 | +int32_t HcommBatchModeEnd(const char* batchTag) | ||
| 100 | +{ | ||
| 101 | + (void)batchTag; | ||
| 102 | + return 0; | ||
| 103 | +} | ||
| 104 | + | ||
| 105 | +} // extern "C" | ||
| @@ -0,0 +1,14 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 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 "log.h" | ||
| 12 | + | ||
| 13 | +bool HcclCheckLogLevel(int logType, int moduleId) { return false; } | ||
| 14 | +bool IsErrorToWarn() { return false; } | ||