已开启
[fix] change topomatch1d topomatch to onelevel #2770
acoolntly创建于 12 天前
[fix] change topomatch1d topomatch to onelevel #2770
已开启
共 37 个文件变更+2260-32
| @@ -96,15 +96,16 @@ HcclResult haclrtGetCaptureInfo(aclrtStream stream, aclmdlRICaptureStatus& captu | |||
| 96 | return HCCL_SUCCESS; | 96 | return HCCL_SUCCESS; |
| 97 | } | 97 | } |
| 98 | 98 | ||
| 99 | -HcclResult hcalrtGetDeviceInfo(u32 deviceId, aclrtDevAttr devAttr, s64& val) | 99 | +HcclResult hcalrtGetDeviceInfo(u32 deviceId, aclrtDevAttr devAttr, s64& val, bool quiet) |
| 100 | { | 100 | { |
| 101 | 101 | ||
| 102 | - static const std::set<aclrtDevAttr> supportType | 102 | + static const std::set<aclrtDevAttr> supportType = { |
| 103 | - = {{ACL_DEV_ATTR_PHY_CHIP_ID}, | 103 | + {ACL_DEV_ATTR_PHY_CHIP_ID}, {ACL_DEV_ATTR_SUPER_POD_DEVIDE_ID}, {ACL_DEV_ATTR_SUPER_POD_SERVER_ID}, |
| 104 | - {ACL_DEV_ATTR_SUPER_POD_DEVIDE_ID}, | 104 | + {ACL_DEV_ATTR_SUPER_POD_ID}, {ACL_DEV_ATTR_CUST_OP_PRIVILEGE}, |
| 105 | - {ACL_DEV_ATTR_SUPER_POD_SERVER_ID}, | 105 | +#if HCCL_SUPPORT_DEV_FORM_FACTOR |
| 106 | - {ACL_DEV_ATTR_SUPER_POD_ID}, | 106 | + {ACL_DEV_ATTR_DEVICE_FORM_FACTOR}, |
| 107 | - {ACL_DEV_ATTR_CUST_OP_PRIVILEGE}}; | 107 | +#endif |
| 108 | + }; | ||
| 108 | 109 | ||
| 109 | auto it = supportType.find(devAttr); | 110 | auto it = supportType.find(devAttr); |
| 110 | CHK_PRT_RET( | 111 | CHK_PRT_RET( |
| @@ -112,10 +113,19 @@ HcclResult hcalrtGetDeviceInfo(u32 deviceId, aclrtDevAttr devAttr, s64& val) | |||
| 112 | HCCL_E_NOT_SUPPORT); | 113 | HCCL_E_NOT_SUPPORT); |
| 113 | 114 | ||
| 114 | aclError ret = aclrtGetDeviceInfo(deviceId, devAttr, reinterpret_cast<int64_t*>(&val)); | 115 | aclError ret = aclrtGetDeviceInfo(deviceId, devAttr, reinterpret_cast<int64_t*>(&val)); |
| 115 | - CHK_PRT_RET( | 116 | + if (ret != ACL_SUCCESS) { |
| 116 | - ret != ACL_SUCCESS, | 117 | + // quiet用于"取不到就降级"的可选属性: 老驱动不支持某个infoType时会稳定失败, |
| 117 | - HCCL_ERROR("[hcalrtGetDeviceInfo]rt get device info failed. ret[%d], attr[%d], val[%ld]", ret, devAttr, val), | 118 | + // 按ERROR打会在完全正常的老环境上持续刷错误日志。传quiet的调用方必须自己处理返回值 |
| 118 | - HCCL_E_RUNTIME); | 119 | + if (quiet) { |
| 120 | + HCCL_WARNING( | ||
| 121 | + "[hcalrtGetDeviceInfo]rt get device info failed. ret[%d], attr[%d]. caller will fall back.", ret, | ||
| 122 | + devAttr); | ||
| 123 | + } else { | ||
| 124 | + HCCL_ERROR( | ||
| 125 | + "[hcalrtGetDeviceInfo]rt get device info failed. ret[%d], attr[%d], val[%ld]", ret, devAttr, val); | ||
| 126 | + } | ||
| 127 | + return HCCL_E_RUNTIME; | ||
| 128 | + } | ||
| 119 | HCCL_DEBUG("Call aclrtGetDeviceInfo, ret[%d], attr[%d], val[%ld]", ret, devAttr, val); | 129 | HCCL_DEBUG("Call aclrtGetDeviceInfo, ret[%d], attr[%d], val[%ld]", ret, devAttr, val); |
| 120 | 130 | ||
| 121 | return HCCL_SUCCESS; | 131 | return HCCL_SUCCESS; |
| @@ -17,6 +17,18 @@ | |||
| 17 | 17 | ||
| 18 | 18 | ||
| 19 | 19 | ||
| 20 | +/* ACL_DEV_ATTR_DEVICE_FORM_FACTOR是新版acl_rt.h才有的枚举值, 老CANN上引用会编译不过。 | ||
| 21 | + * 枚举对预处理器不可见, 因此探测与它同批引入、且只为它服务的宏ACL_DEVICE_FORM_FACTOR_POD。 | ||
| 22 | + * 允许外部预置该宏以强制关闭(降级为一律非POD), 详见physical_levels_design.md。 | ||
| 23 | + */ | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + | ||
| 20 | namespace ops_hccl { | 32 | namespace ops_hccl { |
| 21 | 33 | ||
| 22 | 34 | ||
| @@ -38,7 +50,11 @@ haclrtGetCaptureInfo(aclrtStream stream, aclmdlRICaptureStatus& captureStatus, u | |||
| 38 | 50 | ||
| 39 | HcclResult haclrtGetDeviceIndexByPhyId(u32 devicePhyId, u32& deviceLogicId); | 51 | HcclResult haclrtGetDeviceIndexByPhyId(u32 devicePhyId, u32& deviceLogicId); |
| 40 | 52 | ||
| 41 | -HcclResult hcalrtGetDeviceInfo(u32 deviceId, aclrtDevAttr devAttr, s64& val); | 53 | +/** |
| 54 | + * @param quiet 取不到时是否降噪。默认false保持原有行为(按ERROR打); 对"取不到就走降级值"的可选属性 | ||
| 55 | + * 传true, 失败改按WARNING打。传true的调用方必须自己判返回值并给出降级值。 | ||
| 56 | + */ | ||
| 57 | +HcclResult hcalrtGetDeviceInfo(u32 deviceId, aclrtDevAttr devAttr, s64& val, bool quiet = false); | ||
| 42 | 58 | ||
| 43 | HcclResult LoadBinaryFromFile( | 59 | HcclResult LoadBinaryFromFile( |
| 44 | const char* binPath, aclrtBinaryLoadOptionType optionType, uint32_t cpuKernelMode, aclrtBinHandle& binHandle); | 60 | const char* binPath, aclrtBinaryLoadOptionType optionType, uint32_t cpuKernelMode, aclrtBinHandle& binHandle); |
| @@ -14,6 +14,7 @@ | |||
| 14 | 14 | ||
| 15 | 15 | ||
| 16 | 16 | ||
| 17 | + | ||
| 17 | 18 | ||
| 18 | 19 | ||
| 19 | 20 | ||
| @@ -319,11 +320,11 @@ REGISTER_EXEC_V2( | |||
| 319 | InsTempAllGatherNHR); | 320 | InsTempAllGatherNHR); |
| 320 | 321 | ||
| 321 | REGISTER_EXEC_V2( | 322 | REGISTER_EXEC_V2( |
| 322 | - HcclCMDType::HCCL_CMD_ALLGATHER, DpuAllGatherSoleNHR, InsV2AllGatherSoleExecutor, TopoMatch1D, | 323 | + HcclCMDType::HCCL_CMD_ALLGATHER, DpuAllGatherSoleNHR, InsV2AllGatherSoleExecutor, TopoMatchOneLevel, |
| 323 | InsTempAllGatherNHRDPU); | 324 | InsTempAllGatherNHRDPU); |
| 324 | 325 | ||
| 325 | REGISTER_EXEC_V2( | 326 | REGISTER_EXEC_V2( |
| 326 | - HcclCMDType::HCCL_CMD_ALLGATHER, AicpuAllGatherSoleNHRMultiLink, InsV2AllGatherSoleExecutor, TopoMatch1D, | 327 | + HcclCMDType::HCCL_CMD_ALLGATHER, AicpuAllGatherSoleNHRMultiLink, InsV2AllGatherSoleExecutor, TopoMatchOneLevel, |
| 327 | InsTempAllGatherNHR); | 328 | InsTempAllGatherNHR); |
| 328 | 329 | ||
| 329 | 330 | ||
| @@ -361,7 +362,7 @@ REGISTER_EXEC_V2( | |||
| 361 | 362 | ||
| 362 | 363 | ||
| 363 | REGISTER_EXEC_V2( | 364 | REGISTER_EXEC_V2( |
| 364 | - HcclCMDType::HCCL_CMD_ALLGATHER, CcuSchedAllGatherSoleNHRMultiLink, InsV2AllGatherSoleExecutor, TopoMatch1D, | 365 | + HcclCMDType::HCCL_CMD_ALLGATHER, CcuSchedAllGatherSoleNHRMultiLink, InsV2AllGatherSoleExecutor, TopoMatchOneLevel, |
| 365 | CcuTempAllGatherNHR1DMultiJettyMem2Mem); | 366 | CcuTempAllGatherNHR1DMultiJettyMem2Mem); |
| 366 | 367 | ||
| 367 | 368 | ||
| @@ -115,7 +115,12 @@ InsAllReduceParallelExecutor<AlgTopoMatch, InsAlgTemplate0, InsAlgTemplate1, Ins | |||
| 115 | { | 115 | { |
| 116 | // 使用topo match计算AlgHierarchyInfoForAllLevel | 116 | // 使用topo match计算AlgHierarchyInfoForAllLevel |
| 117 | AlgTopoMatch topoMatch; | 117 | AlgTopoMatch topoMatch; |
| 118 | - CHK_RET(topoMatch.MatchTopo(comm, topoInfo, algHierarchyInfo)); | 118 | + if constexpr (std::is_base_of_v<TopoMatchBaseV2, AlgTopoMatch>) { |
| 119 | + AlgorithmProfile profile = BuildAlgorithmProfile(algName_); | ||
| 120 | + CHK_RET(topoMatch.MatchTopo(comm, topoInfo, algHierarchyInfo, profile)); | ||
| 121 | + } else { | ||
| 122 | + CHK_RET(topoMatch.MatchTopo(comm, topoInfo, algHierarchyInfo)); | ||
| 123 | + } | ||
| 119 | return HCCL_SUCCESS; | 124 | return HCCL_SUCCESS; |
| 120 | } | 125 | } |
| 121 | 126 | ||
| @@ -1365,13 +1370,13 @@ InsAllReduceParallelExecutor<AlgTopoMatch, InsAlgTemplate0, InsAlgTemplate1, Ins | |||
| 1365 | // 算法注册 | 1370 | // 算法注册 |
| 1366 | 1371 | ||
| 1367 | REGISTER_EXECUTOR_BY_FOUR_TEMPS( | 1372 | REGISTER_EXECUTOR_BY_FOUR_TEMPS( |
| 1368 | - HcclCMDType::HCCL_CMD_ALLREDUCE, AicpuAllReduceParallelMeshNHR, InsAllReduceParallelExecutor, TopoMatchMultilevel, | 1373 | + HcclCMDType::HCCL_CMD_ALLREDUCE, AicpuAllReduceParallelMeshNHR, InsAllReduceParallelExecutor, TopoMatchTwoLevel, |
| 1369 | InsTempReduceScatterMesh1D, InsTempReduceScatterNHR, InsTempAllGatherMesh1D, InsTempAllGatherNHR); | 1374 | InsTempReduceScatterMesh1D, InsTempReduceScatterNHR, InsTempAllGatherMesh1D, InsTempAllGatherNHR); |
| 1370 | REGISTER_EXECUTOR_BY_FOUR_TEMPS( | 1375 | REGISTER_EXECUTOR_BY_FOUR_TEMPS( |
| 1371 | HcclCMDType::HCCL_CMD_ALLREDUCE, InsAllReduceParallelMesh1DNHRPcie, InsAllReduceParallelExecutor, TopoMatchPcieMix, | 1376 | HcclCMDType::HCCL_CMD_ALLREDUCE, InsAllReduceParallelMesh1DNHRPcie, InsAllReduceParallelExecutor, TopoMatchPcieMix, |
| 1372 | InsTempReduceScatterMesh1D, InsTempReduceScatterNHR, InsTempAllGatherMesh1D, InsTempAllGatherNHR); | 1377 | InsTempReduceScatterMesh1D, InsTempReduceScatterNHR, InsTempAllGatherMesh1D, InsTempAllGatherNHR); |
| 1373 | REGISTER_EXECUTOR_BY_FOUR_TEMPS( | 1378 | REGISTER_EXECUTOR_BY_FOUR_TEMPS( |
| 1374 | - HcclCMDType::HCCL_CMD_ALLREDUCE, InsAllReduceParallelRSAGUBX, InsAllReduceParallelExecutor, TopoMatchUBX, | 1379 | + HcclCMDType::HCCL_CMD_ALLREDUCE, InsAllReduceParallelRSAGUBX, InsAllReduceParallelExecutor, TopoMatchTwoLevel, |
| 1375 | InsTempReduceScatterMesh1D, InsTempReduceScatterNHR, InsTempAllGatherMesh1D, InsTempAllGatherNHR); | 1380 | InsTempReduceScatterMesh1D, InsTempReduceScatterNHR, InsTempAllGatherMesh1D, InsTempAllGatherNHR); |
| 1376 | 1381 | ||
| 1377 | REGISTER_EXECUTOR_BY_FOUR_TEMPS( | 1382 | REGISTER_EXECUTOR_BY_FOUR_TEMPS( |
| @@ -1386,7 +1391,7 @@ REGISTER_EXECUTOR_BY_FOUR_TEMPS( | |||
| 1386 | 1391 | ||
| 1387 | REGISTER_EXECUTOR_BY_FOUR_TEMPS( | 1392 | REGISTER_EXECUTOR_BY_FOUR_TEMPS( |
| 1388 | HcclCMDType::HCCL_CMD_ALLREDUCE, CcuSchedAllReduceParallelMeshNHR, InsAllReduceParallelExecutor, | 1393 | HcclCMDType::HCCL_CMD_ALLREDUCE, CcuSchedAllReduceParallelMeshNHR, InsAllReduceParallelExecutor, |
| 1389 | - TopoMatchMultilevel, CcuTempReduceScatterMesh1DMem2Mem, CcuTempReduceScatterNHR1DMem2Mem, | 1394 | + TopoMatchTwoLevel, CcuTempReduceScatterMesh1DMem2Mem, CcuTempReduceScatterNHR1DMem2Mem, |
| 1390 | CcuTempAllGatherMesh1DMem2Mem, CcuTempAllGatherNHR1DMem2Mem); | 1395 | CcuTempAllGatherMesh1DMem2Mem, CcuTempAllGatherNHR1DMem2Mem); |
| 1391 | REGISTER_EXECUTOR_BY_FOUR_TEMPS( | 1396 | REGISTER_EXECUTOR_BY_FOUR_TEMPS( |
| 1392 | HcclCMDType::HCCL_CMD_ALLREDUCE, CcuAllReduceParallelNHR1DMutiJetty, InsAllReduceParallelExecutor, TopoMatchUBX, | 1397 | HcclCMDType::HCCL_CMD_ALLREDUCE, CcuAllReduceParallelNHR1DMutiJetty, InsAllReduceParallelExecutor, TopoMatchUBX, |
| @@ -23,6 +23,9 @@ | |||
| 23 | 23 | ||
| 24 | 24 | ||
| 25 | 25 | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 26 | 29 | ||
| 27 | 30 | ||
| 28 | namespace ops_hccl { | 31 | namespace ops_hccl { |
| @@ -113,7 +113,12 @@ InsV2AllReduceSequenceExecutor<AlgTopoMatch, InsAlgTemplate0, InsAlgTemplate1, I | |||
| 113 | devType_ = topoInfo->deviceType; | 113 | devType_ = topoInfo->deviceType; |
| 114 | // 使用topo match计算AlgHierarchyInfoForAllLevel | 114 | // 使用topo match计算AlgHierarchyInfoForAllLevel |
| 115 | AlgTopoMatch topoMatch; | 115 | AlgTopoMatch topoMatch; |
| 116 | - CHK_RET(topoMatch.MatchTopo(comm, topoInfo, algHierarchyInfo)); | 116 | + if constexpr (std::is_base_of_v<TopoMatchBaseV2, AlgTopoMatch>) { |
| 117 | + AlgorithmProfile profile = BuildAlgorithmProfile(algName_); | ||
| 118 | + CHK_RET(topoMatch.MatchTopo(comm, topoInfo, algHierarchyInfo, profile)); | ||
| 119 | + } else { | ||
| 120 | + CHK_RET(topoMatch.MatchTopo(comm, topoInfo, algHierarchyInfo)); | ||
| 121 | + } | ||
| 117 | return HCCL_SUCCESS; | 122 | return HCCL_SUCCESS; |
| 118 | } | 123 | } |
| 119 | 124 | ||
| @@ -509,7 +514,7 @@ InsV2AllReduceSequenceExecutor<AlgTopoMatch, InsAlgTemplate0, InsAlgTemplate1, I | |||
| 509 | } | 514 | } |
| 510 | 515 | ||
| 511 | REGISTER_EXECUTOR_BY_FOUR_TEMPS( | 516 | REGISTER_EXECUTOR_BY_FOUR_TEMPS( |
| 512 | - HcclCMDType::HCCL_CMD_ALLREDUCE, DpuAllReduceSequenceMeshNHR, InsV2AllReduceSequenceExecutor, TopoMatchMultilevel, | 517 | + HcclCMDType::HCCL_CMD_ALLREDUCE, DpuAllReduceSequenceMeshNHR, InsV2AllReduceSequenceExecutor, TopoMatchTwoLevel, |
| 513 | InsTempReduceScatterMesh1DIntra, InsTempReduceScatterMesh1dDpuInter, InsTempAllGatherNhrDpuInter, | 518 | InsTempReduceScatterMesh1DIntra, InsTempReduceScatterMesh1dDpuInter, InsTempAllGatherNhrDpuInter, |
| 514 | InsTempAllGatherMesh1dIntra); | 519 | InsTempAllGatherMesh1dIntra); |
| 515 | } // namespace ops_hccl | 520 | } // namespace ops_hccl |
| @@ -22,6 +22,9 @@ | |||
| 22 | 22 | ||
| 23 | 23 | ||
| 24 | 24 | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 25 | 28 | ||
| 26 | namespace ops_hccl { | 29 | namespace ops_hccl { |
| 27 | 30 | ||
| @@ -120,7 +120,12 @@ InsV2AllReduceSequenceExecutorAicpu<AlgTopoMatch, InsAlgTemplate0, InsAlgTemplat | |||
| 120 | rankSize_ = topoInfo->userRankSize; | 120 | rankSize_ = topoInfo->userRankSize; |
| 121 | // 使用topo match计算AlgHierarchyInfoForAllLevel | 121 | // 使用topo match计算AlgHierarchyInfoForAllLevel |
| 122 | AlgTopoMatch topoMatch; | 122 | AlgTopoMatch topoMatch; |
| 123 | - CHK_RET(topoMatch.MatchTopo(comm, topoInfo, algHierarchyInfo)); | 123 | + if constexpr (std::is_base_of_v<TopoMatchBaseV2, AlgTopoMatch>) { |
| 124 | + AlgorithmProfile profile = BuildAlgorithmProfile(algName_); | ||
| 125 | + CHK_RET(topoMatch.MatchTopo(comm, topoInfo, algHierarchyInfo, profile)); | ||
| 126 | + } else { | ||
| 127 | + CHK_RET(topoMatch.MatchTopo(comm, topoInfo, algHierarchyInfo)); | ||
| 128 | + } | ||
| 124 | return HCCL_SUCCESS; | 129 | return HCCL_SUCCESS; |
| 125 | } | 130 | } |
| 126 | 131 | ||
| @@ -773,7 +778,7 @@ InsV2AllReduceSequenceExecutorAicpu<AlgTopoMatch, InsAlgTemplate0, InsAlgTemplat | |||
| 773 | 778 | ||
| 774 | REGISTER_EXECUTOR_BY_FOUR_TEMPS( | 779 | REGISTER_EXECUTOR_BY_FOUR_TEMPS( |
| 775 | HcclCMDType::HCCL_CMD_ALLREDUCE, AicpuAllReduceSequenceMeshConcurNHR, InsV2AllReduceSequenceExecutorAicpu, | 780 | HcclCMDType::HCCL_CMD_ALLREDUCE, AicpuAllReduceSequenceMeshConcurNHR, InsV2AllReduceSequenceExecutorAicpu, |
| 776 | - TopoMatchMultilevel, InsTempReduceScatterMesh1DZAxisDetour, InsTempReduceScatterNHR, InsTempAllGatherNHR, | 781 | + TopoMatchTwoLevel, InsTempReduceScatterMesh1DZAxisDetour, InsTempReduceScatterNHR, InsTempAllGatherNHR, |
| 777 | InsTempAllGatherMesh1D1DZAxisDetour); | 782 | InsTempAllGatherMesh1D1DZAxisDetour); |
| 778 | 783 | ||
| 779 | 784 | ||
| @@ -22,6 +22,9 @@ | |||
| 22 | 22 | ||
| 23 | 23 | ||
| 24 | 24 | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 25 | 28 | ||
| 26 | namespace ops_hccl { | 29 | namespace ops_hccl { |
| 27 | 30 | ||
| @@ -126,7 +126,12 @@ HcclResult InsV2AllReduceSequenceExecutorAicpu3Level< | |||
| 126 | myRank_ = topoInfo->userRank; | 126 | myRank_ = topoInfo->userRank; |
| 127 | rankSize_ = topoInfo->userRankSize; | 127 | rankSize_ = topoInfo->userRankSize; |
| 128 | AlgTopoMatch topoMatch; | 128 | AlgTopoMatch topoMatch; |
| 129 | - CHK_RET(topoMatch.MatchTopo(comm, topoInfo, algHierarchyInfo)); | 129 | + if constexpr (std::is_base_of_v<TopoMatchBaseV2, AlgTopoMatch>) { |
| 130 | + AlgorithmProfile profile = BuildAlgorithmProfile(algName_); | ||
| 131 | + CHK_RET(topoMatch.MatchTopo(comm, topoInfo, algHierarchyInfo, profile)); | ||
| 132 | + } else { | ||
| 133 | + CHK_RET(topoMatch.MatchTopo(comm, topoInfo, algHierarchyInfo)); | ||
| 134 | + } | ||
| 130 | return HCCL_SUCCESS; | 135 | return HCCL_SUCCESS; |
| 131 | } | 136 | } |
| 132 | 137 | ||
| @@ -739,7 +744,7 @@ HcclResult InsV2AllReduceSequenceExecutorAicpu3Level< | |||
| 739 | 744 | ||
| 740 | REGISTER_EXEC_V2_MULTI( | 745 | REGISTER_EXEC_V2_MULTI( |
| 741 | HcclCMDType::HCCL_CMD_ALLREDUCE, AicpuAllReduceSequenceMeshConcurNHRNHR, InsV2AllReduceSequenceExecutorAicpu3Level, | 746 | HcclCMDType::HCCL_CMD_ALLREDUCE, AicpuAllReduceSequenceMeshConcurNHRNHR, InsV2AllReduceSequenceExecutorAicpu3Level, |
| 742 | - TopoMatchMultilevel, InsTempReduceScatterMesh1DZAxisDetour, InsTempReduceScatterNHR, InsTempReduceScatterNHR, | 747 | + TopoMatchThreeLevel, InsTempReduceScatterMesh1DZAxisDetour, InsTempReduceScatterNHR, InsTempReduceScatterNHR, |
| 743 | InsTempAllGatherNHR, InsTempAllGatherNHR, InsTempAllGatherMesh1D1DZAxisDetour); | 748 | InsTempAllGatherNHR, InsTempAllGatherNHR, InsTempAllGatherMesh1D1DZAxisDetour); |
| 744 | 749 | ||
| 745 | } // namespace ops_hccl | 750 | } // namespace ops_hccl |
| @@ -22,6 +22,9 @@ | |||
| 22 | 22 | ||
| 23 | 23 | ||
| 24 | 24 | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 25 | 28 | ||
| 26 | namespace ops_hccl { | 29 | namespace ops_hccl { |
| 27 | 30 | ||
| @@ -69,7 +69,12 @@ HcclResult InsV2AllReduceSoleExecutor<AlgTopoMatch, InsAlgTemplate>::CalcAlgHier | |||
| 69 | { | 69 | { |
| 70 | // 使用topo match计算AlgHierarchyInfoForAllLevel | 70 | // 使用topo match计算AlgHierarchyInfoForAllLevel |
| 71 | AlgTopoMatch topoMatch; | 71 | AlgTopoMatch topoMatch; |
| 72 | - CHK_RET(topoMatch.MatchTopo(comm, topoInfo, algHierarchyInfo)); | 72 | + if constexpr (std::is_base_of_v<TopoMatchBaseV2, AlgTopoMatch>) { |
| 73 | + AlgorithmProfile profile = BuildAlgorithmProfile(algName_); | ||
| 74 | + CHK_RET(topoMatch.MatchTopo(comm, topoInfo, algHierarchyInfo, profile)); | ||
| 75 | + } else { | ||
| 76 | + CHK_RET(topoMatch.MatchTopo(comm, topoInfo, algHierarchyInfo)); | ||
| 77 | + } | ||
| 73 | return HCCL_SUCCESS; | 78 | return HCCL_SUCCESS; |
| 74 | } | 79 | } |
| 75 | 80 | ||
| @@ -296,10 +301,10 @@ HcclResult InsV2AllReduceSoleExecutor<AlgTopoMatch, InsAlgTemplate>::FastLaunch( | |||
| 296 | 301 | ||
| 297 | 302 | ||
| 298 | REGISTER_EXEC_V2( | 303 | REGISTER_EXEC_V2( |
| 299 | - HcclCMDType::HCCL_CMD_ALLREDUCE, AicpuAllReduceSoleMeshOneShot, InsV2AllReduceSoleExecutor, TopoMatch1D, | 304 | + HcclCMDType::HCCL_CMD_ALLREDUCE, AicpuAllReduceSoleMeshOneShot, InsV2AllReduceSoleExecutor, TopoMatchOneLevel, |
| 300 | InsTempAllReduceMesh1DOneShot); | 305 | InsTempAllReduceMesh1DOneShot); |
| 301 | REGISTER_EXEC_V2( | 306 | REGISTER_EXEC_V2( |
| 302 | - HcclCMDType::HCCL_CMD_ALLREDUCE, AicpuAllReduceSoleMeshTwoShot, InsV2AllReduceSoleExecutor, TopoMatch1D, | 307 | + HcclCMDType::HCCL_CMD_ALLREDUCE, AicpuAllReduceSoleMeshTwoShot, InsV2AllReduceSoleExecutor, TopoMatchOneLevel, |
| 303 | InsTempAllReduceMesh1DTwoShot); | 308 | InsTempAllReduceMesh1DTwoShot); |
| 304 | REGISTER_EXEC_V2( | 309 | REGISTER_EXEC_V2( |
| 305 | HcclCMDType::HCCL_CMD_ALLREDUCE, AicpuAllReduceSoleNHR, InsV2AllReduceSoleExecutor, TopoMatch1D, | 310 | HcclCMDType::HCCL_CMD_ALLREDUCE, AicpuAllReduceSoleNHR, InsV2AllReduceSoleExecutor, TopoMatch1D, |
| @@ -308,7 +313,7 @@ REGISTER_EXEC_V2( | |||
| 308 | HcclCMDType::HCCL_CMD_ALLREDUCE, AicpuAllReduceSoleNHRTwoShotMultiLink, InsV2AllReduceSoleExecutor, TopoMatch1D, | 313 | HcclCMDType::HCCL_CMD_ALLREDUCE, AicpuAllReduceSoleNHRTwoShotMultiLink, InsV2AllReduceSoleExecutor, TopoMatch1D, |
| 309 | InsTempAllReduceNHR); | 314 | InsTempAllReduceNHR); |
| 310 | REGISTER_EXEC_V2( | 315 | REGISTER_EXEC_V2( |
| 311 | - HcclCMDType::HCCL_CMD_ALLREDUCE, AicpuAllReduceSoleMeshChunkTwoShot, InsV2AllReduceSoleExecutor, TopoMatch1D, | 316 | + HcclCMDType::HCCL_CMD_ALLREDUCE, AicpuAllReduceSoleMeshChunkTwoShot, InsV2AllReduceSoleExecutor, TopoMatchOneLevel, |
| 312 | InsTempAllReduceMesh1DTwoShotMeshChunk); | 317 | InsTempAllReduceMesh1DTwoShotMeshChunk); |
| 313 | REGISTER_EXEC_V2( | 318 | REGISTER_EXEC_V2( |
| 314 | HcclCMDType::HCCL_CMD_ALLREDUCE, AicpuAllReduceSoleNHRAicpuReduce, InsV2AllReduceSoleExecutor, TopoMatch1D, | 319 | HcclCMDType::HCCL_CMD_ALLREDUCE, AicpuAllReduceSoleNHRAicpuReduce, InsV2AllReduceSoleExecutor, TopoMatch1D, |
| @@ -14,6 +14,9 @@ | |||
| 14 | 14 | ||
| 15 | 15 | ||
| 16 | 16 | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 17 | 20 | ||
| 18 | namespace ops_hccl { | 21 | namespace ops_hccl { |
| 19 | template <typename AlgTopoMatch, typename InsAlgTemplate> | 22 | template <typename AlgTopoMatch, typename InsAlgTemplate> |
| @@ -79,7 +79,12 @@ HcclResult InsV2AllReduceTwoShotSoleExecutor<AlgTopoMatch, InsAlgTemplate0, InsA | |||
| 79 | myRank_ = topoInfo->userRank; | 79 | myRank_ = topoInfo->userRank; |
| 80 | rankSize_ = topoInfo->userRankSize; | 80 | rankSize_ = topoInfo->userRankSize; |
| 81 | AlgTopoMatch topoMatch; | 81 | AlgTopoMatch topoMatch; |
| 82 | - CHK_RET(topoMatch.MatchTopo(comm, topoInfo, algHierarchyInfo)); | 82 | + if constexpr (std::is_base_of_v<TopoMatchBaseV2, AlgTopoMatch>) { |
| 83 | + AlgorithmProfile profile = BuildAlgorithmProfile(algName_); | ||
| 84 | + CHK_RET(topoMatch.MatchTopo(comm, topoInfo, algHierarchyInfo, profile)); | ||
| 85 | + } else { | ||
| 86 | + CHK_RET(topoMatch.MatchTopo(comm, topoInfo, algHierarchyInfo)); | ||
| 87 | + } | ||
| 83 | return HCCL_SUCCESS; | 88 | return HCCL_SUCCESS; |
| 84 | } | 89 | } |
| 85 | 90 | ||
| @@ -309,6 +314,6 @@ HcclResult InsV2AllReduceTwoShotSoleExecutor<AlgTopoMatch, InsAlgTemplate0, InsA | |||
| 309 | } | 314 | } |
| 310 | 315 | ||
| 311 | REGISTER_EXECUTOR_BY_TWO_TEMPS( | 316 | REGISTER_EXECUTOR_BY_TWO_TEMPS( |
| 312 | - HcclCMDType::HCCL_CMD_ALLREDUCE, AicpuAllReduceSoleMeshConcur, InsV2AllReduceTwoShotSoleExecutor, TopoMatch1D, | 317 | + HcclCMDType::HCCL_CMD_ALLREDUCE, AicpuAllReduceSoleMeshConcur, InsV2AllReduceTwoShotSoleExecutor, TopoMatchOneLevel, |
| 313 | InsTempReduceScatterMesh1DZAxisDetour, InsTempAllGatherMesh1D1DZAxisDetour); | 318 | InsTempReduceScatterMesh1DZAxisDetour, InsTempAllGatherMesh1D1DZAxisDetour); |
| 314 | } // namespace ops_hccl | 319 | } // namespace ops_hccl |
| @@ -14,6 +14,9 @@ | |||
| 14 | 14 | ||
| 15 | 15 | ||
| 16 | 16 | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 17 | 20 | ||
| 18 | namespace ops_hccl { | 21 | namespace ops_hccl { |
| 19 | 22 | ||
| @@ -23,6 +23,39 @@ std::string InsCollAlgBase::Describe() const | |||
| 23 | return s; | 23 | return s; |
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | +u32 InsCollAlgBase::GetPhysicalLevelNetLayer(const TopoInfoWithNetLayerDetails* topoInfo, u32 levelIdx) const | ||
| 27 | +{ | ||
| 28 | + // 不用CHK_PTR_NULL: 它返回HcclResult, 在这个返回u32的函数里会被隐式转成一个看起来像层号的 | ||
| 29 | + // 小整数(HCCL_E_PTR是3), 静默当成netLayer[3]用 | ||
| 30 | + if (topoInfo == nullptr) { | ||
| 31 | + HCCL_WARNING("[InsCollAlgBase][GetPhysicalLevelNetLayer] topoInfo is null"); | ||
| 32 | + return INVALID_UINT; | ||
| 33 | + } | ||
| 34 | + if (levelIdx >= topoInfo->physicalLevels.size()) { | ||
| 35 | + HCCL_WARNING( | ||
| 36 | + "[InsCollAlgBase][GetPhysicalLevelNetLayer] levelIdx[%u] out of range, physicalLevelNum[%zu]", levelIdx, | ||
| 37 | + topoInfo->physicalLevels.size()); | ||
| 38 | + return INVALID_UINT; | ||
| 39 | + } | ||
| 40 | + return topoInfo->physicalLevels[levelIdx].ref.netLayer; | ||
| 41 | +} | ||
| 42 | + | ||
| 43 | +std::vector<u32> | ||
| 44 | +InsCollAlgBase::GetPhysicalLevelPortNums(const TopoInfoWithNetLayerDetails* topoInfo, u32 levelIdx) const | ||
| 45 | +{ | ||
| 46 | + if (topoInfo == nullptr) { | ||
| 47 | + HCCL_WARNING("[InsCollAlgBase][GetPhysicalLevelPortNums] topoInfo is null"); | ||
| 48 | + return std::vector<u32>(); | ||
| 49 | + } | ||
| 50 | + if (levelIdx >= topoInfo->physicalLevels.size()) { | ||
| 51 | + HCCL_WARNING( | ||
| 52 | + "[InsCollAlgBase][GetPhysicalLevelPortNums] levelIdx[%u] out of range, physicalLevelNum[%zu]", levelIdx, | ||
| 53 | + topoInfo->physicalLevels.size()); | ||
| 54 | + return std::vector<u32>(); | ||
| 55 | + } | ||
| 56 | + return topoInfo->physicalLevels[levelIdx].portNums; | ||
| 57 | +} | ||
| 58 | + | ||
| 26 | HcclResult InsCollAlgBase::RestoreChannelMap( | 59 | HcclResult InsCollAlgBase::RestoreChannelMap( |
| 27 | const AlgResourceCtxSerializable& resCtx, | 60 | const AlgResourceCtxSerializable& resCtx, |
| 28 | std::vector<std::map<u32, std::vector<ChannelInfo>>>& rankIdToChannelInfo) const | 61 | std::vector<std::map<u32, std::vector<ChannelInfo>>>& rankIdToChannelInfo) const |
| @@ -66,6 +66,9 @@ public: | |||
| 66 | HcclResult SetTempFastLaunchAddr( | 66 | HcclResult SetTempFastLaunchAddr( |
| 67 | TemplateFastLaunchCtx& tempFastLaunchCtx, void* inputPtr, void* outputPtr, const HcclMem& hcclBuff) const; | 67 | TemplateFastLaunchCtx& tempFastLaunchCtx, void* inputPtr, void* outputPtr, const HcclMem& hcclBuff) const; |
| 68 | 68 | ||
| 69 | + // 由 op_common 在 CalcAlgHierarchyInfo 调用前设置算法名,供 V2 MatchTopo 构造 AlgorithmProfile | ||
| 70 | + inline void SetAlgName(const std::string& name) { algName_ = name; } | ||
| 71 | + | ||
| 69 | virtual HcclResult RestoreChannelMap( | 72 | virtual HcclResult RestoreChannelMap( |
| 70 | const AlgResourceCtxSerializable& resCtx, | 73 | const AlgResourceCtxSerializable& resCtx, |
| 71 | std::vector<std::map<u32, std::vector<ChannelInfo>>>& rankIdToChannelInfo) const; | 74 | std::vector<std::map<u32, std::vector<ChannelInfo>>>& rankIdToChannelInfo) const; |
| @@ -80,6 +83,20 @@ public: | |||
| 80 | u32 notifyNumOnMainThread) const; | 83 | u32 notifyNumOnMainThread) const; |
| 81 | 84 | ||
| 82 | protected: | 85 | protected: |
| 86 | + /* | ||
| 87 | + * 读取physicalLevels[levelIdx]的netLayer编号。Level下标与netLayer编号没有固定对应关系, | ||
| 88 | + * 一个netLayer可能贡献一级或两级, 要拿netLayer必须走这里回查, 不能拿下标当层号用。 | ||
| 89 | + * 下标越界或physicalLevels为空(标准化降级)时返回INVALID_UINT并告警。 | ||
| 90 | + */ | ||
| 91 | + u32 GetPhysicalLevelNetLayer(const TopoInfoWithNetLayerDetails* topoInfo, u32 levelIdx) const; | ||
| 92 | + | ||
| 93 | + /* | ||
| 94 | + * 读取physicalLevels[levelIdx]上本卡各条物理链路的端口数, 降序, 按iface去重。 | ||
| 95 | + * 局部量: 同一级上各rank可能不同, 不能用它做跨rank一致的决策。 | ||
| 96 | + * 返回空统一表示"端口数不可用"(越界/无TopoInstance/采集降级), 不表示该级有0个端口。 | ||
| 97 | + */ | ||
| 98 | + std::vector<u32> GetPhysicalLevelPortNums(const TopoInfoWithNetLayerDetails* topoInfo, u32 levelIdx) const; | ||
| 99 | + | ||
| 83 | inline void SetOrderPreservedBaseParams(const OrderPreservedBaseParams& params) | 100 | inline void SetOrderPreservedBaseParams(const OrderPreservedBaseParams& params) |
| 84 | { | 101 | { |
| 85 | myRank_ = params.myRank; | 102 | myRank_ = params.myRank; |
| @@ -110,6 +127,9 @@ protected: | |||
| 110 | // dataSize | 127 | // dataSize |
| 111 | u64 dataSize_ = 0; | 128 | u64 dataSize_ = 0; |
| 112 | u64 dataTypeSize_ = 0; | 129 | u64 dataTypeSize_ = 0; |
| 130 | + | ||
| 131 | + // 算法名,由 SetAlgName 在 CalcAlgHierarchyInfo 调用前设置,供 MatchTopo 构造 AlgorithmProfile | ||
| 132 | + std::string algName_; | ||
| 113 | }; | 133 | }; |
| 114 | 134 | ||
| 115 | } // namespace ops_hccl | 135 | } // namespace ops_hccl |
| @@ -18,6 +18,7 @@ | |||
| 18 | 18 | ||
| 19 | 19 | ||
| 20 | 20 | ||
| 21 | + | ||
| 21 | 22 | ||
| 22 | 23 | ||
| 23 | 24 | ||
| @@ -40,6 +41,9 @@ constexpr u32 MAX_NUM_BLOCKS = 56; // 56-72 | |||
| 40 | 41 | ||
| 41 | constexpr u32 HCCL_LOGIC_TOPO_LEVEL_NUM = 4; // HCCL逻辑拓扑层级最多4级 | 42 | constexpr u32 HCCL_LOGIC_TOPO_LEVEL_NUM = 4; // HCCL逻辑拓扑层级最多4级 |
| 42 | 43 | ||
| 44 | +// physicalLevels的条目数上界。定位是"超过即判定字节流不可信"的合理性阈值, 不是业务上限 | ||
| 45 | +constexpr u32 PHYSICAL_LEVEL_NUM_LIMIT = 10; | ||
| 46 | + | ||
| 43 | constexpr uint32_t DATATYPE_SIZE_TABLE[HCCL_DATA_TYPE_RESERVED] | 47 | constexpr uint32_t DATATYPE_SIZE_TABLE[HCCL_DATA_TYPE_RESERVED] |
| 44 | = {sizeof(int8_t), | 48 | = {sizeof(int8_t), |
| 45 | sizeof(int16_t), | 49 | sizeof(int16_t), |
| @@ -180,6 +184,68 @@ struct TopoInstDetails { | |||
| 180 | std::map<CommTopo, std::vector<u32>> rankNumForTopoType; | 184 | std::map<CommTopo, std::vector<u32>> rankNumForTopoType; |
| 181 | }; | 185 | }; |
| 182 | 186 | ||
| 187 | +/* | ||
| 188 | + * PhysicalLevel: 把RankGraph的NetInstance(ranktable给出的全局分区)与TopoInstance(topo文件给出的 | ||
| 189 | + * 链路属性)合成一条"当前rank可见、从小到大"的物理范围链, 供TopoMatch与建链侧统一消费。 | ||
| 190 | + * 构建规则、排序三键、跨rank一致性与降级约定见 physical_levels_design.md。 | ||
| 191 | + */ | ||
| 192 | + | ||
| 193 | +// 该Level是否知道整个通信域在这个粒度上的完整划分。这是RankGraph两组接口的能力差异, 无法互相推导: | ||
| 194 | +// 只有GetInstSizeListByLayer看得到兄弟NetInstance, GetTopoInstsByLayer只看得到本rank所在的那一个 | ||
| 195 | +enum class PhysicalLevelView : u32 { | ||
| 196 | + LOCAL = 0, // 只知道当前rank所在的那一块; instSizeListByLayer恒为空 | ||
| 197 | + GLOBAL = 1, // 知道该netLayer的完整分区; instSizeListByLayer非空 | ||
| 198 | +}; | ||
| 199 | + | ||
| 200 | +// 该Level在RankGraph中的原始身份, 用于回查。 | ||
| 201 | +// netLayer恒有效(每个Level必然归属某一层); topoInstId在该Level有TopoInstance支撑时才有效 | ||
| 202 | +struct PhysicalSourceRef { | ||
| 203 | + u32 netLayer = INVALID_UINT; | ||
| 204 | + u32 topoInstId = INVALID_UINT; | ||
| 205 | +}; | ||
| 206 | + | ||
| 207 | +// 范围链上的一环。整条链按三键排序, 相邻两环的rank集合满足包含关系(可以相等) | ||
| 208 | +struct PhysicalLevelInfo { | ||
| 209 | + // 当前rank在该范围内可见的全部rank, 升序去重, 必然含当前rank。 | ||
| 210 | + // 局部量: 同一级上不同rank看到的集合不同(rank 0看到{0..7}, rank 9看到{8..15}), | ||
| 211 | + // 但同一集合内的各rank看到的内容完全一致 | ||
| 212 | + std::vector<u32> localRanks; | ||
| 213 | + PhysicalLevelView view = PhysicalLevelView::LOCAL; | ||
| 214 | + // 该netLayer上全部NetInstance的大小, 按最小rankId升序, 即一份分区布局; view为LOCAL时恒为空。 | ||
| 215 | + // 原样透传HcclRankGraphGetInstSizeListByLayer的返回序, 不重排 —— 重排会毁掉布局语义。 | ||
| 216 | + // 全局量, 跨rank逐字节相同, 是本结构唯一可用的跨rank一致性锚点 | ||
| 217 | + std::vector<u32> instSizeListByLayer; | ||
| 218 | + PhysicalSourceRef ref; | ||
| 219 | + | ||
| 220 | + // ---- 以下为链路属性: 由该Level的TopoInstance提供, 全部随hasTopoInst一起生效 ---- | ||
| 221 | + | ||
| 222 | + // 该Level有无TopoInstance支撑。false时下面全部链路属性无意义, 各自保持无效值 | ||
| 223 | + bool hasTopoInst = false; | ||
| 224 | + // 互联形态。同时是排序第三键: netLayer 0上同范围的Mesh与CLOS靠它定序, | ||
| 225 | + // 因此有TopoInstance却取不到topoType时必须整体降级, 不能保留RESERVED继续 | ||
| 226 | + CommTopo topoType = CommTopo::COMM_TOPO_RESERVED; | ||
| 227 | + // 该Level的链路落在Device还是Host。消费侧据此判断"是否需要使用host网卡"(看最高一级)。 | ||
| 228 | + // 各endpoint位置不一致时保持RESERVED并告警 —— 一个Level对应一种网络平面, 位置本应唯一 | ||
| 229 | + EndpointLocType locType = EndpointLocType::ENDPOINT_LOC_TYPE_RESERVED; | ||
| 230 | + // 该Level上出现的协议集合, 去重升序。是集合而不是单值: 同一个iface可以同时跑多种协议 | ||
| 231 | + // (如ub_ctp与ub_mem), HCOMM侧会为每种协议各生成一个EndpointDesc但它们指向同一个iface | ||
| 232 | + std::vector<CommProtocol> protocols; | ||
| 233 | + // 该Level上本卡各条物理链路的端口数, 降序, 按iface(commAddr)去重, 求和为本卡在该级的总端口数。 | ||
| 234 | + // 取自ENDPOINT_ATTR_BW_COEFF, HCOMM侧实现即iface->GetPorts().size()。 | ||
| 235 | + // 局部量, 跨rank不保证相同; 空表示未取到(见FetchPortNums), 不是"0个端口" | ||
| 236 | + std::vector<u32> portNums; | ||
| 237 | + // 当前rank在该Level上的Endpoint快照, 供建链侧回查。已按(protocol, locType, addr)排序: | ||
| 238 | + // 原始返回是哈希序, 不排序会导致同一拓扑在不同进程下得到不同的字节流 | ||
| 239 | + std::vector<EndpointDesc> endpoints; | ||
| 240 | +}; | ||
| 241 | + | ||
| 242 | +/* | ||
| 243 | + * endpoints走BinaryStream的整块裸拷贝, 只对POD正确。EndpointDesc将来若引入变长成员(如std::string), | ||
| 244 | + * 写进流的会是堆指针而不是内容, 且不报错、只在远处随机崩溃。这条断言让那种改动直接编译失败。 | ||
| 245 | + */ | ||
| 246 | +static_assert( | ||
| 247 | + std::is_trivially_copyable<EndpointDesc>::value, "EndpointDesc must be trivially copyable for serialization"); | ||
| 248 | + | ||
| 183 | 249 | ||
| 184 | 250 | ||
| 185 | typedef struct { | 251 | typedef struct { |
| @@ -211,6 +277,33 @@ struct TopoInfo { | |||
| 211 | bool isHCCSSWNumEqualToTwiceSIONum = false; // A3 Server内链路属性 | 277 | bool isHCCSSWNumEqualToTwiceSIONum = false; // A3 Server内链路属性 |
| 212 | ThreadHandle mainThread; // 主流对应threadHandle | 278 | ThreadHandle mainThread; // 主流对应threadHandle |
| 213 | u32 notifyNumOnMainThread = 0; // mainThread上创建的notify数量 | 279 | u32 notifyNumOnMainThread = 0; // mainThread上创建的notify数量 |
| 280 | + }; | ||
| 281 | + | ||
| 282 | +enum class PhysicalLevelIndex : uint32_t { | ||
| 283 | + PHYSICAL_LEVEL_IDX_0, | ||
| 284 | + PHYSICAL_LEVEL_IDX_1, | ||
| 285 | + PHYSICAL_LEVEL_IDX_2, | ||
| 286 | + PHYSICAL_LEVEL_IDX_3, | ||
| 287 | + PHYSICAL_LEVEL_IDX_4, | ||
| 288 | + PHYSICAL_LEVEL_IDX_5, | ||
| 289 | + PHYSICAL_LEVEL_IDX_6, | ||
| 290 | + PHYSICAL_LEVEL_IDX_7, | ||
| 291 | + PHYSICAL_LEVEL_IDX_8, | ||
| 292 | + PHYSICAL_LEVEL_IDX_9, | ||
| 293 | +}; | ||
| 294 | + | ||
| 295 | +enum class AlgoType : uint8_t { | ||
| 296 | + MESH, MESH_2DIE, MESH_ONESHOT, MESH_TWOSHOT, MESH_CONCUR, MESH_MULTILINK, | ||
| 297 | + MESH_CHUNK, MESH_CHUNK_TWOSHOT, NHR, NHR_MULTILINK, NHR_AICPU_REDUCE, | ||
| 298 | + MESH_SINGLE_CHANNEL, MESH_CONCURRENT, UNKNOWN, | ||
| 299 | +}; | ||
| 300 | + | ||
| 301 | +// 当前调用 MatchTopo 的算法属性,MatchTopo 据此判断算法与拓扑是否匹配 | ||
| 302 | +struct AlgorithmProfile { | ||
| 303 | + std::string name; | ||
| 304 | + HcclCMDType opType = HcclCMDType::HCCL_CMD_INVALID; | ||
| 305 | + OpExecuteConfig engine = OpExecuteConfig::DEFAULT; | ||
| 306 | + std::vector<AlgoType> algoTypes; // 元素数 = 算法层数(OneLevel=1, TwoLevel=2, ThreeLevel=3) | ||
| 214 | }; | 307 | }; |
| 215 | 308 | ||
| 216 | // 这个应该是公共的 | 309 | // 这个应该是公共的 |
| @@ -229,9 +322,18 @@ struct TopoInfoWithNetLayerDetails : public TopoInfo { // 通信域拓扑ctx | |||
| 229 | bool level0Symmetric{false}; | 322 | bool level0Symmetric{false}; |
| 230 | bool level1Symmetric{false}; | 323 | bool level1Symmetric{false}; |
| 231 | u32 topoInstDetailsOfLayerSize = 0; | 324 | u32 topoInstDetailsOfLayerSize = 0; |
| 325 | + // 本卡是否为POD机型, 由CalcDeviceFormFactor查ACL_DEV_ATTR_DEVICE_FORM_FACTOR得到, 取不到停在false。 | ||
| 326 | + // 局部量: 由本rank查本卡得到, 异构组网下各rank可能不同, 消费侧需自行处理 | ||
| 327 | + bool isPod = false; | ||
| 232 | Level0MeshType level0MeshType; | 328 | Level0MeshType level0MeshType; |
| 233 | NetLayerDetails netLayerDetails; | 329 | NetLayerDetails netLayerDetails; |
| 234 | std::vector<TopoInstDetails> topoInstDetailsOfLayer; | 330 | std::vector<TopoInstDetails> topoInstDetailsOfLayer; |
| 331 | + // physicalLevels的条目数, 由Serialize统一回填。独立成字段是为了对齐topoInstDetailsOfLayerSize | ||
| 332 | + // 的既有约定: 反序列化侧先拿到一个可校验的上界, 再据此决定是否resize | ||
| 333 | + u32 physicalLevelNum = 0; | ||
| 334 | + // 标准化后的物理范围链, 按范围从小到大稠密排列。为空表示降级, 消费侧回退旧路径。 | ||
| 335 | + // 必须放在结构体尾部, 序列化也只能追加在尾部, 否则会改变既有字段的字节偏移 | ||
| 336 | + std::vector<PhysicalLevelInfo> physicalLevels; | ||
| 235 | 337 | ||
| 236 | std::vector<char> Serialize() | 338 | std::vector<char> Serialize() |
| 237 | { | 339 | { |
| @@ -280,6 +382,24 @@ struct TopoInfoWithNetLayerDetails : public TopoInfo { // 通信域拓扑ctx | |||
| 280 | binaryStream << topoInstDetailsOfLayer[idx].ranksInTopo; | 382 | binaryStream << topoInstDetailsOfLayer[idx].ranksInTopo; |
| 281 | binaryStream << topoInstDetailsOfLayer[idx].rankNumForTopoType; | 383 | binaryStream << topoInstDetailsOfLayer[idx].rankNumForTopoType; |
| 282 | } | 384 | } |
| 385 | + physicalLevelNum = static_cast<u32>(physicalLevels.size()); | ||
| 386 | + binaryStream << physicalLevelNum; | ||
| 387 | + for (const auto& level : physicalLevels) { | ||
| 388 | + binaryStream << level.localRanks; | ||
| 389 | + binaryStream << level.view; | ||
| 390 | + binaryStream << level.instSizeListByLayer; | ||
| 391 | + binaryStream << level.ref.netLayer; | ||
| 392 | + binaryStream << level.ref.topoInstId; | ||
| 393 | + binaryStream << level.hasTopoInst; | ||
| 394 | + binaryStream << level.topoType; | ||
| 395 | + binaryStream << level.locType; | ||
| 396 | + binaryStream << level.protocols; | ||
| 397 | + binaryStream << level.portNums; | ||
| 398 | + binaryStream << level.endpoints; | ||
| 399 | + } | ||
| 400 | + // 声明在标量区但序列化追加在尾部: 守的是physicalLevels"只能追加在尾部"的约定, | ||
| 401 | + // DeSerialize侧必须保持同样的顺序 | ||
| 402 | + binaryStream << isPod; | ||
| 283 | std::vector<char> result; | 403 | std::vector<char> result; |
| 284 | binaryStream.Dump(result); | 404 | binaryStream.Dump(result); |
| 285 | return result; | 405 | return result; |
| @@ -336,6 +456,34 @@ struct TopoInfoWithNetLayerDetails : public TopoInfo { // 通信域拓扑ctx | |||
| 336 | binaryStream >> topoInstDetailsOfLayer[idx].ranksInTopo; | 456 | binaryStream >> topoInstDetailsOfLayer[idx].ranksInTopo; |
| 337 | binaryStream >> topoInstDetailsOfLayer[idx].rankNumForTopoType; | 457 | binaryStream >> topoInstDetailsOfLayer[idx].rankNumForTopoType; |
| 338 | } | 458 | } |
| 459 | + physicalLevelNum = 0; | ||
| 460 | + physicalLevels.clear(); | ||
| 461 | + isPod = false; | ||
| 462 | + binaryStream >> physicalLevelNum; | ||
| 463 | + if (physicalLevelNum > PHYSICAL_LEVEL_NUM_LIMIT) { | ||
| 464 | + HCCL_WARNING( | ||
| 465 | + "[TopoInfo][DeSerialize] implausible physicalLevelNum[%u], drop the whole physical level section", | ||
| 466 | + physicalLevelNum); | ||
| 467 | + physicalLevelNum = 0; | ||
| 468 | + // 这里返回会连带丢掉后面的isPod, 使其停在false。流是纯位置流, 跳过变长段就无法定位其后的 | ||
| 469 | + // 字段, 这是尾部追加的必然代价; 两者的降级态都是"该字段不可用", 消费侧本就必须处理 | ||
| 470 | + return; | ||
| 471 | + } | ||
| 472 | + physicalLevels.resize(physicalLevelNum); | ||
| 473 | + for (auto& level : physicalLevels) { | ||
| 474 | + binaryStream >> level.localRanks; | ||
| 475 | + binaryStream >> level.view; | ||
| 476 | + binaryStream >> level.instSizeListByLayer; | ||
| 477 | + binaryStream >> level.ref.netLayer; | ||
| 478 | + binaryStream >> level.ref.topoInstId; | ||
| 479 | + binaryStream >> level.hasTopoInst; | ||
| 480 | + binaryStream >> level.topoType; | ||
| 481 | + binaryStream >> level.locType; | ||
| 482 | + binaryStream >> level.protocols; | ||
| 483 | + binaryStream >> level.portNums; | ||
| 484 | + binaryStream >> level.endpoints; | ||
| 485 | + } | ||
| 486 | + binaryStream >> isPod; | ||
| 339 | } | 487 | } |
| 340 | }; | 488 | }; |
| 341 | 489 | ||
| @@ -467,6 +615,7 @@ struct AlgResourceCtx { | |||
| 467 | // 如果能够序列化那么就是下面的结构体 | 615 | // 如果能够序列化那么就是下面的结构体 |
| 468 | struct AlgHierarchyInfoForAllLevel { | 616 | struct AlgHierarchyInfoForAllLevel { |
| 469 | std::vector<std::vector<std::vector<u32>>> infos; // 第一维表示有多少level,第二维是每个level的rankID | 617 | std::vector<std::vector<std::vector<u32>>> infos; // 第一维表示有多少level,第二维是每个level的rankID |
| 618 | + std::vector<std::vector<PhysicalLevelIndex>> physicalIdxForAlgoLevels; // 每个算法层可对应多个物理层 | ||
| 470 | }; | 619 | }; |
| 471 | // 如果能够序列化那么就是下面的结构体 | 620 | // 如果能够序列化那么就是下面的结构体 |
| 472 | // 先序列化,把东西考到device,然后把指针存到OpParam,在device侧反序列该指针执行的内存 | 621 | // 先序列化,把东西考到device,然后把指针存到OpParam,在device侧反序列该指针执行的内存 |
| @@ -502,6 +651,7 @@ struct AlgResourceCtxSerializable { | |||
| 502 | 651 | ||
| 503 | binaryStream << algType; | 652 | binaryStream << algType; |
| 504 | binaryStream << algHierarchyInfo.infos; | 653 | binaryStream << algHierarchyInfo.infos; |
| 654 | + binaryStream << algHierarchyInfo.physicalIdxForAlgoLevels; | ||
| 505 | binaryStream << cclMem; | 655 | binaryStream << cclMem; |
| 506 | binaryStream << notifyNumOnMainThread; | 656 | binaryStream << notifyNumOnMainThread; |
| 507 | binaryStream << slaveThreadNum; | 657 | binaryStream << slaveThreadNum; |
| @@ -537,6 +687,7 @@ struct AlgResourceCtxSerializable { | |||
| 537 | 687 | ||
| 538 | binaryStream >> algType; | 688 | binaryStream >> algType; |
| 539 | binaryStream >> algHierarchyInfo.infos; | 689 | binaryStream >> algHierarchyInfo.infos; |
| 690 | + binaryStream >> algHierarchyInfo.physicalIdxForAlgoLevels; | ||
| 540 | binaryStream >> cclMem; | 691 | binaryStream >> cclMem; |
| 541 | binaryStream >> notifyNumOnMainThread; | 692 | binaryStream >> notifyNumOnMainThread; |
| 542 | binaryStream >> slaveThreadNum; | 693 | binaryStream >> slaveThreadNum; |
| @@ -789,6 +789,7 @@ HcclResult GeReuseResource( | |||
| 789 | { | 789 | { |
| 790 | // 计算AlgHierarchyInfo | 790 | // 计算AlgHierarchyInfo |
| 791 | AlgHierarchyInfoForAllLevel algHierarchyInfo; // 分级通信域信息{localRankId, localRankSize} | 791 | AlgHierarchyInfoForAllLevel algHierarchyInfo; // 分级通信域信息{localRankId, localRankSize} |
| 792 | + executor->SetAlgName(param.algName); | ||
| 792 | CHK_RET(executor->CalcAlgHierarchyInfo(comm, topoInfo, algHierarchyInfo)); | 793 | CHK_RET(executor->CalcAlgHierarchyInfo(comm, topoInfo, algHierarchyInfo)); |
| 793 | // 资源计算 | 794 | // 资源计算 |
| 794 | AlgResourceRequest resRequest; | 795 | AlgResourceRequest resRequest; |
| @@ -1218,6 +1219,7 @@ HcclResult HcclGetAlgRes( | |||
| 1218 | 1219 | ||
| 1219 | // 计算AlgHierarchyInfo | 1220 | // 计算AlgHierarchyInfo |
| 1220 | AlgHierarchyInfoForAllLevel algHierarchyInfo; // 分级通信域信息{localRankId, localRankSize} | 1221 | AlgHierarchyInfoForAllLevel algHierarchyInfo; // 分级通信域信息{localRankId, localRankSize} |
| 1222 | + executor->SetAlgName(param.algName); | ||
| 1221 | CHK_RET(executor->CalcAlgHierarchyInfo(comm, topoInfo, algHierarchyInfo)); | 1223 | CHK_RET(executor->CalcAlgHierarchyInfo(comm, topoInfo, algHierarchyInfo)); |
| 1222 | // 资源计算 | 1224 | // 资源计算 |
| 1223 | HCCL_INFO("[HcclGetAlgRes] executor->CalcRes."); | 1225 | HCCL_INFO("[HcclGetAlgRes] executor->CalcRes."); |
| @@ -13,7 +13,15 @@ set(src_list | |||
| 13 | ${CMAKE_CURRENT_SOURCE_DIR}/topo_host.cc | 13 | ${CMAKE_CURRENT_SOURCE_DIR}/topo_host.cc |
| 14 | ${CMAKE_CURRENT_SOURCE_DIR}/topo_match_1d.cc | 14 | ${CMAKE_CURRENT_SOURCE_DIR}/topo_match_1d.cc |
| 15 | ${CMAKE_CURRENT_SOURCE_DIR}/topo_match_base.cc | 15 | ${CMAKE_CURRENT_SOURCE_DIR}/topo_match_base.cc |
| 16 | + ${CMAKE_CURRENT_SOURCE_DIR}/topo_match_base_v2.cc | ||
| 16 | ${CMAKE_CURRENT_SOURCE_DIR}/topo_match_concurrent.cc | 17 | ${CMAKE_CURRENT_SOURCE_DIR}/topo_match_concurrent.cc |
| 18 | + # PhysicalLevel标准化: 不依赖版本宏, 因此放在无条件块 | ||
| 19 | + ${CMAKE_CURRENT_SOURCE_DIR}/physical_level_build.cc | ||
| 20 | + ${CMAKE_CURRENT_SOURCE_DIR}/physical_level_normalize.cc | ||
| 21 | + ${CMAKE_CURRENT_SOURCE_DIR}/topo_match_one_level.cc | ||
| 22 | + ${CMAKE_CURRENT_SOURCE_DIR}/topo_match_two_level.cc | ||
| 23 | + ${CMAKE_CURRENT_SOURCE_DIR}/topo_match_three_level.cc | ||
| 24 | + ${CMAKE_CURRENT_SOURCE_DIR}/topo_match_concurrent_v2.cc | ||
| 17 | ) | 25 | ) |
| 18 | if(NOT HCCL_CANN_COMPAT_850) | 26 | if(NOT HCCL_CANN_COMPAT_850) |
| 19 | list(APPEND src_list | 27 | list(APPEND src_list |
| @@ -0,0 +1,64 @@ | |||
| 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 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | +namespace ops_hccl { | ||
| 19 | + | ||
| 20 | +// Endpoint数量的合理性阈值, 不是防御性截断: 该值是纯本地量(接口数 x 协议数), 不随rankSize增长, | ||
| 21 | +// 超过该量级只可能是HCOMM侧异常, 此时局部降级并告警 | ||
| 22 | +constexpr u32 ENDPOINT_NUM_SANITY_LIMIT = 64; | ||
| 23 | + | ||
| 24 | +// 单个端口组的端口数合理性阈值。HCOMM侧MAX_PORT_NUM是32, 驱动侧UB口上限36, | ||
| 25 | +// 超过该量级说明ENDPOINT_ATTR_BW_COEFF返回的不是端口数, 整个Level的portNums不可信 | ||
| 26 | +constexpr u32 PORT_NUM_SANITY_LIMIT = 64; | ||
| 27 | + | ||
| 28 | +// ---- 纯函数: 不依赖HcclComm与RankGraph, 可离线UT (physical_level_normalize.cc) ---- | ||
| 29 | + | ||
| 30 | +/** | ||
| 31 | + * EndpointDesc的稳定排序键。GetEndpointDesc的输出是哈希序, 必须归一化后再保存。 | ||
| 32 | + * 按字段比较而不是memcmp整个结构体: 尾部raws在HCOMM侧从未赋值。 | ||
| 33 | + */ | ||
| 34 | +bool EndpointDescLess(const EndpointDesc& lhs, const EndpointDesc& rhs); | ||
| 35 | + | ||
| 36 | +/** | ||
| 37 | + * 两个EndpointDesc是否指向同一个iface。判据是commAddr —— HCOMM的endpointToIfaceMap以 | ||
| 38 | + * (commAddr, protocol)为键, 同addr不同protocol必然映射到同一个iface。用于按链路统计端口数。 | ||
| 39 | + */ | ||
| 40 | +bool CommAddrEqual(const CommAddr& lhs, const CommAddr& rhs); | ||
| 41 | + | ||
| 42 | +/** | ||
| 43 | + * 候选范围的标准化: 归一 -> 三键排序 -> 范围链校验, 不做合并。candidates按值语义被移动消耗。 | ||
| 44 | + * 返回HCCL_E_NOT_SUPPORT表示不构成范围链或排序键取不到值, 由调用方降级。 | ||
| 45 | + */ | ||
| 46 | +HcclResult NormalizePhysicalLevels( | ||
| 47 | + std::vector<PhysicalLevelInfo>& candidates, u32 userRank, u32 userRankSize, std::vector<PhysicalLevelInfo>& levels); | ||
| 48 | + | ||
| 49 | +/** | ||
| 50 | + * 标准化结果的一致性校验, 逐条对应physical_levels_design.md中的不变量。 | ||
| 51 | + */ | ||
| 52 | +HcclResult ValidatePhysicalLevels(const std::vector<PhysicalLevelInfo>& levels, u32 userRank, u32 userRankSize); | ||
| 53 | + | ||
| 54 | +// ---- 依赖HcclComm (physical_level_build.cc) ---- | ||
| 55 | + | ||
| 56 | +/** | ||
| 57 | + * 构建topoInfo->physicalLevels。任何失败一律降级为空视图并返回HCCL_SUCCESS, | ||
| 58 | + * 绝不改变CalcTopoShape的返回值。 | ||
| 59 | + */ | ||
| 60 | +HcclResult BuildPhysicalLevels(HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo); | ||
| 61 | + | ||
| 62 | +} // namespace ops_hccl | ||
| 63 | + | ||
| 64 | + | ||
| @@ -0,0 +1,460 @@ | |||
| 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 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | +namespace ops_hccl { | ||
| 22 | +namespace { | ||
| 23 | + | ||
| 24 | + // 日志里一个vector最多展开的元素数, 超出部分省略 | ||
| 25 | + constexpr size_t LOG_VEC_MAX_ITEM = 16; | ||
| 26 | + | ||
| 27 | + std::string VecToStr(const std::vector<u32>& vec) | ||
| 28 | + { | ||
| 29 | + std::string str; | ||
| 30 | + for (size_t i = 0; i < vec.size(); ++i) { | ||
| 31 | + if (i >= LOG_VEC_MAX_ITEM) { | ||
| 32 | + str += ",..."; | ||
| 33 | + break; | ||
| 34 | + } | ||
| 35 | + if (i > 0) { | ||
| 36 | + str += ","; | ||
| 37 | + } | ||
| 38 | + str += std::to_string(vec[i]); | ||
| 39 | + } | ||
| 40 | + return str; | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + // 把一个Level拼成一行日志。localRanks只打首尾与个数, 不整条展开: 顶层那一级等于整个通信域, | ||
| 44 | + // 万卡场景整条打出来没人看得完, 而升序与含myRank由校验侧保证 | ||
| 45 | + std::string DescribeLevel(const PhysicalLevelInfo& level, size_t idx, size_t total) | ||
| 46 | + { | ||
| 47 | + return "level[" + std::to_string(idx) + "/" + std::to_string(total) + "] rankNum[" | ||
| 48 | + + std::to_string(level.localRanks.size()) + "] ranks[" | ||
| 49 | + + (level.localRanks.empty() ? | ||
| 50 | + std::string("-") : | ||
| 51 | + std::to_string(level.localRanks.front()) + ".." + std::to_string(level.localRanks.back())) | ||
| 52 | + + "] view[" + std::to_string(static_cast<u32>(level.view)) + "] instSizeListByLayer[" | ||
| 53 | + + VecToStr(level.instSizeListByLayer) + "] ref[layer " + std::to_string(level.ref.netLayer) + " inst " | ||
| 54 | + + std::to_string(level.ref.topoInstId) + "] hasTopoInst[" + std::to_string(level.hasTopoInst ? 1 : 0) | ||
| 55 | + + "] topoType[" + std::to_string(static_cast<s32>(level.topoType)) + "] locType[" | ||
| 56 | + + std::to_string(static_cast<s32>(level.locType)) + "] protocolNum[" | ||
| 57 | + + std::to_string(level.protocols.size()) + "] portNums[" + VecToStr(level.portNums) + "]"; | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + /** | ||
| 61 | + * 提取当前rank在指定TopoInstance上的Endpoint快照。 | ||
| 62 | + * 返回void: endpoints是payload叶子, 不参与排序键与范围链结构, 全部失败路径都局部降级为空。 | ||
| 63 | + */ | ||
| 64 | + void FetchEndpoints(HcclComm comm, u32 layer, u32 instId, std::vector<EndpointDesc>& out) | ||
| 65 | + { | ||
| 66 | + out.clear(); | ||
| 67 | + u32 num = 0; | ||
| 68 | + // 与下面的num == 0分开判: 取数失败是真异常, 需要留日志 | ||
| 69 | + if (HcclRankGraphGetEndpointNum(comm, layer, instId, &num) != HCCL_SUCCESS) { | ||
| 70 | + HCCL_WARNING( | ||
| 71 | + "[PhysicalLevel][Build] get endpoint num failed at layer[%u] inst[%u], skip endpoints", layer, instId); | ||
| 72 | + return; | ||
| 73 | + } | ||
| 74 | + // 0是合法结果, 不是错误: 当前rank在该topoInst上没有接口/协议时就是0 | ||
| 75 | + if (num == 0) { | ||
| 76 | + HCCL_DEBUG("[PhysicalLevel][Build] no endpoint at layer[%u] inst[%u]", layer, instId); | ||
| 77 | + return; | ||
| 78 | + } | ||
| 79 | + // 合理性阈值, 不截断: 截断只会把异常掩盖成"正常但数据少", 直接局部降级并告警 | ||
| 80 | + if (num > ENDPOINT_NUM_SANITY_LIMIT) { | ||
| 81 | + HCCL_WARNING( | ||
| 82 | + "[PhysicalLevel][Build] implausible endpoint num[%u] at layer[%u] inst[%u], skip endpoints", num, layer, | ||
| 83 | + instId); | ||
| 84 | + return; | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + // num是实际写入条数的上界(GetEndpointNum求和时不去重), 必须以回写的descNum为准resize | ||
| 88 | + std::vector<EndpointDesc> buf(num); | ||
| 89 | + u32 actualNum = num; | ||
| 90 | + if (HcclRankGraphGetEndpointDesc(comm, layer, instId, &actualNum, buf.data()) != HCCL_SUCCESS) { | ||
| 91 | + HCCL_WARNING( | ||
| 92 | + "[PhysicalLevel][Build] get endpoint desc failed at layer[%u] inst[%u], skip endpoints", layer, instId); | ||
| 93 | + return; | ||
| 94 | + } | ||
| 95 | + if (actualNum > num) { | ||
| 96 | + HCCL_WARNING( | ||
| 97 | + "[PhysicalLevel][Build] endpoint descNum[%u] exceeds requested[%u] at layer[%u] inst[%u], skip " | ||
| 98 | + "endpoints", | ||
| 99 | + actualNum, num, layer, instId); | ||
| 100 | + return; | ||
| 101 | + } | ||
| 102 | + buf.resize(actualNum); | ||
| 103 | + // GetEndpointDesc的输出顺序是unordered_map哈希序, 必须归一化后再保存 | ||
| 104 | + std::sort(buf.begin(), buf.end(), EndpointDescLess); | ||
| 105 | + out = std::move(buf); | ||
| 106 | + } | ||
| 107 | + | ||
| 108 | + /** | ||
| 109 | + * 采集本rank在该Level上各条物理链路的端口数, 降序写入out。按iface(commAddr)去重, 一条链路一项: | ||
| 110 | + * 一个iface有N种协议就有N个EndpointDesc, 逐endpoint查会把同一条链路的端口数重复计入。 | ||
| 111 | + * 全有或全无: 任一条取不到就整个清空 —— 残缺数组会让消费侧算出"看着合理但偏小"的总端口数。 | ||
| 112 | + */ | ||
| 113 | + void FetchPortNums(HcclComm comm, u32 myRank, const std::vector<EndpointDesc>& endpoints, std::vector<u32>& out) | ||
| 114 | + { | ||
| 115 | + out.clear(); | ||
| 116 | + // 空有两种来源: 本就没有接口, 或FetchEndpoints已降级(含HCOMM低版本弱符号未命中), | ||
| 117 | + // 因此这里不需要再做一次能力探测 | ||
| 118 | + if (endpoints.empty()) { | ||
| 119 | + return; | ||
| 120 | + } | ||
| 121 | + | ||
| 122 | + std::vector<CommAddr> seenAddrs; // 已计入的iface。规模是个位数, 线性查找即可 | ||
| 123 | + std::vector<u32> portNums; | ||
| 124 | + for (const auto& desc : endpoints) { | ||
| 125 | + bool seen = false; | ||
| 126 | + for (const auto& addr : seenAddrs) { | ||
| 127 | + if (CommAddrEqual(addr, desc.commAddr)) { | ||
| 128 | + seen = true; | ||
| 129 | + break; | ||
| 130 | + } | ||
| 131 | + } | ||
| 132 | + if (seen) { | ||
| 133 | + continue; // 同一个iface的另一种协议, 端口数已经计过 | ||
| 134 | + } | ||
| 135 | + | ||
| 136 | + EndpointAttrBwCoeff portNum{}; | ||
| 137 | + // ENDPOINT_ATTR_BW_COEFF名为"带宽系数", HCOMM侧实现即iface->GetPorts().size() | ||
| 138 | + if (HcclRankGraphGetEndpointInfo( | ||
| 139 | + comm, myRank, &desc, ENDPOINT_ATTR_BW_COEFF, sizeof(EndpointAttrBwCoeff), &portNum) | ||
| 140 | + != HCCL_SUCCESS) { | ||
| 141 | + HCCL_WARNING( | ||
| 142 | + "[PhysicalLevel][Build] get port num failed for rank[%u] protocol[%d], drop port nums of this " | ||
| 143 | + "level", | ||
| 144 | + myRank, static_cast<s32>(desc.protocol)); | ||
| 145 | + return; | ||
| 146 | + } | ||
| 147 | + // 0与超限都判为不可信, 口径对齐op_common.cc的BuildChannelInfo | ||
| 148 | + if (portNum == 0 || portNum > PORT_NUM_SANITY_LIMIT) { | ||
| 149 | + HCCL_WARNING( | ||
| 150 | + "[PhysicalLevel][Build] implausible port num[%u] for rank[%u] protocol[%d], drop port nums of " | ||
| 151 | + "this level", | ||
| 152 | + portNum, myRank, static_cast<s32>(desc.protocol)); | ||
| 153 | + return; | ||
| 154 | + } | ||
| 155 | + seenAddrs.push_back(desc.commAddr); | ||
| 156 | + portNums.push_back(static_cast<u32>(portNum)); | ||
| 157 | + } | ||
| 158 | + // 降序。与endpoints的(protocol, locType, addr)序无关, 两者是同一批iface的两种独立排列 | ||
| 159 | + std::sort(portNums.begin(), portNums.end(), std::greater<u32>()); | ||
| 160 | + out = std::move(portNums); | ||
| 161 | + } | ||
| 162 | + | ||
| 163 | + /** | ||
| 164 | + * 从endpoints提炼该Level的位置与协议集合。locType各endpoint不一致时置RESERVED并告警: | ||
| 165 | + * 一个Level对应一种网络平面, 位置本应唯一, 给出任一个都会误导"是否需要host网卡"的判断。 | ||
| 166 | + */ | ||
| 167 | + void FetchLocAndProtocols( | ||
| 168 | + const std::vector<EndpointDesc>& endpoints, EndpointLocType& locType, std::vector<CommProtocol>& protocols) | ||
| 169 | + { | ||
| 170 | + locType = EndpointLocType::ENDPOINT_LOC_TYPE_RESERVED; | ||
| 171 | + protocols.clear(); | ||
| 172 | + if (endpoints.empty()) { | ||
| 173 | + return; | ||
| 174 | + } | ||
| 175 | + | ||
| 176 | + locType = endpoints.front().loc.locType; | ||
| 177 | + for (const auto& desc : endpoints) { | ||
| 178 | + if (desc.loc.locType != locType) { | ||
| 179 | + HCCL_WARNING( | ||
| 180 | + "[PhysicalLevel][Build] mixed endpoint locType[%d] vs [%d] on one level, mark location unknown", | ||
| 181 | + static_cast<s32>(desc.loc.locType), static_cast<s32>(locType)); | ||
| 182 | + locType = EndpointLocType::ENDPOINT_LOC_TYPE_RESERVED; | ||
| 183 | + break; | ||
| 184 | + } | ||
| 185 | + } | ||
| 186 | + | ||
| 187 | + protocols.reserve(endpoints.size()); | ||
| 188 | + for (const auto& desc : endpoints) { | ||
| 189 | + protocols.push_back(desc.protocol); | ||
| 190 | + } | ||
| 191 | + // 去重升序: endpoints已按protocol为首键排过, 但同一协议可能出现在多个iface上 | ||
| 192 | + std::sort(protocols.begin(), protocols.end()); | ||
| 193 | + protocols.erase(std::unique(protocols.begin(), protocols.end()), protocols.end()); | ||
| 194 | + } | ||
| 195 | + | ||
| 196 | + // 取该netLayer本地NetInstance的rank集合与全层分区。这是"合一"里ranktable那一半: | ||
| 197 | + // 只有NetInstance看得到兄弟实例, 因此只有它能给出全局分区 | ||
| 198 | + HcclResult FetchNetInstance( | ||
| 199 | + HcclComm comm, const TopoInfoWithNetLayerDetails* topoInfo, u32 layer, std::vector<u32>& ranks, | ||
| 200 | + std::vector<u32>& instSizeListByLayer) | ||
| 201 | + { | ||
| 202 | + const NetLayerDetails& details = topoInfo->netLayerDetails; | ||
| 203 | + const u32 myRank = topoInfo->userRank; | ||
| 204 | + if (layer >= details.localNetInsSizeOfLayer.size() || layer >= details.instSizeListOfLayer.size()) { | ||
| 205 | + HCCL_WARNING("[PhysicalLevel][Build] netLayer[%u] out of range of netLayerDetails arrays", layer); | ||
| 206 | + return HCCL_E_INTERNAL; | ||
| 207 | + } | ||
| 208 | + | ||
| 209 | + u32* rawRanks = nullptr; | ||
| 210 | + u32 rankNum = 0; | ||
| 211 | + if (HcclRankGraphGetRanksByLayer(comm, layer, &rawRanks, &rankNum) != HCCL_SUCCESS || rawRanks == nullptr) { | ||
| 212 | + HCCL_WARNING("[PhysicalLevel][Build] get ranks by layer[%u] failed", layer); | ||
| 213 | + return HCCL_E_INTERNAL; | ||
| 214 | + } | ||
| 215 | + // HCOMM为该接口只持有一个成员vector, 下一次调用会clear()并重填它, 必须立即复制 | ||
| 216 | + ranks.assign(rawRanks, rawRanks + rankNum); | ||
| 217 | + | ||
| 218 | + // 跨调用一致性校验: localNetInsSizeOfLayer来自ExtractNetLayerDetails中的另一次调用, | ||
| 219 | + // 与此处不同源, 不一致说明RankGraph在两次调用之间发生了变化 | ||
| 220 | + if (ranks.size() != details.localNetInsSizeOfLayer[layer]) { | ||
| 221 | + HCCL_WARNING( | ||
| 222 | + "[PhysicalLevel][Build] netLayer[%u] rankNum[%zu] mismatches localNetInsSize[%u]", layer, ranks.size(), | ||
| 223 | + details.localNetInsSizeOfLayer[layer]); | ||
| 224 | + return HCCL_E_INTERNAL; | ||
| 225 | + } | ||
| 226 | + if (std::find(ranks.begin(), ranks.end(), myRank) == ranks.end()) { | ||
| 227 | + HCCL_WARNING( | ||
| 228 | + "[PhysicalLevel][Build] netLayer[%u] local instance does not contain myRank[%u]", layer, myRank); | ||
| 229 | + return HCCL_E_INTERNAL; | ||
| 230 | + } | ||
| 231 | + | ||
| 232 | + // 原样透传HCOMM的返回序, 不重排。该序是"按最小rankId升序的分区布局", | ||
| 233 | + // topo_host.cc的CalcGroupIdx/GetCurrentServerStartRank已在其上做前缀和定位, 必须与之一致 | ||
| 234 | + instSizeListByLayer = details.instSizeListOfLayer[layer]; | ||
| 235 | + if (instSizeListByLayer.empty()) { | ||
| 236 | + HCCL_WARNING("[PhysicalLevel][Build] netLayer[%u] inst size list is empty", layer); | ||
| 237 | + return HCCL_E_INTERNAL; | ||
| 238 | + } | ||
| 239 | + // 哨兵, 正常路径永不触发(ExtractNetLayerDetails已用同一等式先行校验过)。 | ||
| 240 | + // 保留它只为在HCOMM改变分层语义时第一时间暴露 | ||
| 241 | + const u32 totalRankNum = std::accumulate(instSizeListByLayer.begin(), instSizeListByLayer.end(), 0U); | ||
| 242 | + if (totalRankNum != topoInfo->userRankSize) { | ||
| 243 | + HCCL_WARNING( | ||
| 244 | + "[PhysicalLevel][Build] netLayer[%u] inst size sum[%u] mismatches userRankSize[%u]", layer, | ||
| 245 | + totalRankNum, topoInfo->userRankSize); | ||
| 246 | + return HCCL_E_INTERNAL; | ||
| 247 | + } | ||
| 248 | + // 布局自检: 用userRank做前缀和定位到本rank所在的块, 其大小必须等于本地实例的rank数。 | ||
| 249 | + // 两个量来源独立, 对得上才说明"按最小rankId升序"这个布局假设在本层成立 | ||
| 250 | + u32 cumulative = 0; | ||
| 251 | + bool located = false; | ||
| 252 | + for (u32 instSize : instSizeListByLayer) { | ||
| 253 | + cumulative += instSize; | ||
| 254 | + if (myRank < cumulative) { | ||
| 255 | + located = (instSize == static_cast<u32>(ranks.size())); | ||
| 256 | + if (!located) { | ||
| 257 | + HCCL_WARNING( | ||
| 258 | + "[PhysicalLevel][Build] netLayer[%u] rank[%u] locates a block of size[%u] but local " | ||
| 259 | + "instance has [%zu] ranks, inst size list is not laid out by ascending min rankId", | ||
| 260 | + layer, myRank, instSize, ranks.size()); | ||
| 261 | + } | ||
| 262 | + break; | ||
| 263 | + } | ||
| 264 | + } | ||
| 265 | + if (!located) { | ||
| 266 | + return HCCL_E_INTERNAL; | ||
| 267 | + } | ||
| 268 | + return HCCL_SUCCESS; | ||
| 269 | + } | ||
| 270 | + | ||
| 271 | + // 取该netLayer上、含当前rank的每个TopoInstance的rank集合, 并把链路属性填进level。 | ||
| 272 | + // 这是"合一"里topo那一半: 只有TopoInstance带得出形态/位置/协议/端口数 | ||
| 273 | + HcclResult FetchTopoInstances(HcclComm comm, u32 myRank, u32 layer, std::vector<PhysicalLevelInfo>& out) | ||
| 274 | + { | ||
| 275 | + out.clear(); | ||
| 276 | + u32* rawInstIds = nullptr; | ||
| 277 | + u32 instNum = 0; | ||
| 278 | + HcclResult ret = HcclRankGraphGetTopoInstsByLayer(comm, layer, &rawInstIds, &instNum); | ||
| 279 | + if (ret != HCCL_SUCCESS) { | ||
| 280 | + HCCL_WARNING("[PhysicalLevel][Build] get topo insts of layer[%u] failed, ret[%d]", layer, ret); | ||
| 281 | + return HCCL_E_INTERNAL; | ||
| 282 | + } | ||
| 283 | + if (instNum == 0) { | ||
| 284 | + // 空map即返回0且不报错。意味着该层没有endpoints与topoType可供建链, | ||
| 285 | + // 调用方据此把hasTopoInst置false | ||
| 286 | + HCCL_DEBUG("[PhysicalLevel][Build] layer[%u] has no topo instance", layer); | ||
| 287 | + return HCCL_SUCCESS; | ||
| 288 | + } | ||
| 289 | + if (rawInstIds == nullptr) { | ||
| 290 | + HCCL_WARNING("[PhysicalLevel][Build] topo insts of layer[%u] is null while num[%u]", layer, instNum); | ||
| 291 | + return HCCL_E_INTERNAL; | ||
| 292 | + } | ||
| 293 | + // 立即复制: 该接口的下一次调用会clear()并重填同一个成员vector | ||
| 294 | + std::vector<u32> instIds(rawInstIds, rawInstIds + instNum); | ||
| 295 | + | ||
| 296 | + for (u32 instId : instIds) { | ||
| 297 | + u32* rawRanks = nullptr; | ||
| 298 | + u32 rankNum = 0; | ||
| 299 | + if (HcclRankGraphGetRanksByTopoInst(comm, layer, instId, &rawRanks, &rankNum) != HCCL_SUCCESS | ||
| 300 | + || rawRanks == nullptr) { | ||
| 301 | + HCCL_WARNING("[PhysicalLevel][Build] get ranks by topo inst[%u] of layer[%u] failed", instId, layer); | ||
| 302 | + return HCCL_E_INTERNAL; | ||
| 303 | + } | ||
| 304 | + std::vector<u32> ranks(rawRanks, rawRanks + rankNum); | ||
| 305 | + | ||
| 306 | + // GetTopoInstsByLayer返回的应当只含当前rank所在的topoInstance, 这里再过滤一次兜底 | ||
| 307 | + if (std::find(ranks.begin(), ranks.end(), myRank) == ranks.end()) { | ||
| 308 | + HCCL_DEBUG( | ||
| 309 | + "[PhysicalLevel][Build] skip sibling topo inst[%u] of layer[%u], myRank[%u] not in it", instId, | ||
| 310 | + layer, myRank); | ||
| 311 | + continue; | ||
| 312 | + } | ||
| 313 | + | ||
| 314 | + // 必须用按topoInst的GetTopoType。按netLayer的GetTopoTypeByLayer查的是NetType, | ||
| 315 | + // A5上Mesh层是TOPO_FILE_DESC描述的, 会返回COMM_TOPO_CUSTOM, TopoTypeOrder定不了序 | ||
| 316 | + CommTopo topoType = CommTopo::COMM_TOPO_RESERVED; | ||
| 317 | + if (HcclRankGraphGetTopoType(comm, layer, instId, &topoType) != HCCL_SUCCESS) { | ||
| 318 | + HCCL_WARNING("[PhysicalLevel][Build] get topo type of inst[%u] layer[%u] failed", instId, layer); | ||
| 319 | + return HCCL_E_INTERNAL; | ||
| 320 | + } | ||
| 321 | + | ||
| 322 | + PhysicalLevelInfo level; | ||
| 323 | + level.localRanks = std::move(ranks); | ||
| 324 | + level.ref.netLayer = layer; | ||
| 325 | + level.ref.topoInstId = instId; | ||
| 326 | + level.hasTopoInst = true; | ||
| 327 | + level.topoType = topoType; | ||
| 328 | + // 以下三项无返回值: 内部失败一律局部降级, 理由见各自声明处 | ||
| 329 | + FetchEndpoints(comm, layer, instId, level.endpoints); | ||
| 330 | + FetchLocAndProtocols(level.endpoints, level.locType, level.protocols); | ||
| 331 | + FetchPortNums(comm, myRank, level.endpoints, level.portNums); | ||
| 332 | + out.push_back(std::move(level)); | ||
| 333 | + } | ||
| 334 | + return HCCL_SUCCESS; | ||
| 335 | + } | ||
| 336 | + | ||
| 337 | + /** | ||
| 338 | + * 按netLayer把ranktable层级与topo层级合成候选Level。合并规则: | ||
| 339 | + * 同范围的TopoInstance -> 与NetInstance合并成一级(view=GLOBAL); 更小的 -> 独立成级(view=LOCAL); | ||
| 340 | + * 该层没有TopoInstance -> NetInstance独立成级(hasTopoInst=false)。详见physical_levels_design.md。 | ||
| 341 | + */ | ||
| 342 | + HcclResult BuildLayerCandidates( | ||
| 343 | + HcclComm comm, const TopoInfoWithNetLayerDetails* topoInfo, u32 layer, | ||
| 344 | + std::vector<PhysicalLevelInfo>& candidates) | ||
| 345 | + { | ||
| 346 | + const u32 myRank = topoInfo->userRank; | ||
| 347 | + std::vector<u32> netRanks; | ||
| 348 | + std::vector<u32> instSizeListByLayer; | ||
| 349 | + CHK_RET(FetchNetInstance(comm, topoInfo, layer, netRanks, instSizeListByLayer)); | ||
| 350 | + | ||
| 351 | + std::vector<PhysicalLevelInfo> topoLevels; | ||
| 352 | + CHK_RET(FetchTopoInstances(comm, myRank, layer, topoLevels)); | ||
| 353 | + | ||
| 354 | + // 两侧都来自HCOMM且均为升序无重复, 直接比vector即可 | ||
| 355 | + std::vector<u32> sortedNetRanks = netRanks; | ||
| 356 | + std::sort(sortedNetRanks.begin(), sortedNetRanks.end()); | ||
| 357 | + | ||
| 358 | + bool merged = false; | ||
| 359 | + for (auto& level : topoLevels) { | ||
| 360 | + std::vector<u32> sortedTopoRanks = level.localRanks; | ||
| 361 | + std::sort(sortedTopoRanks.begin(), sortedTopoRanks.end()); | ||
| 362 | + if (sortedTopoRanks == sortedNetRanks) { | ||
| 363 | + // 同范围: 把NetInstance的全局分区并进来。分区是该层的全局事实, 同层多个 | ||
| 364 | + // 同范围TopoInstance(如netLayer 0的Mesh与CLOS)各自都持有它 | ||
| 365 | + level.view = PhysicalLevelView::GLOBAL; | ||
| 366 | + level.instSizeListByLayer = instSizeListByLayer; | ||
| 367 | + merged = true; | ||
| 368 | + } else { | ||
| 369 | + // 比NetInstance更细: 看不到兄弟NetInstance, 没有全局分区可言 | ||
| 370 | + level.view = PhysicalLevelView::LOCAL; | ||
| 371 | + level.instSizeListByLayer.clear(); | ||
| 372 | + } | ||
| 373 | + HCCL_DEBUG( | ||
| 374 | + "[PhysicalLevel][Build] layer[%u] inst[%u] rankNum[%zu] view[%u] topoType[%d] locType[%d] " | ||
| 375 | + "protocolNum[%zu] portNumCnt[%zu]", | ||
| 376 | + layer, level.ref.topoInstId, level.localRanks.size(), static_cast<u32>(level.view), | ||
| 377 | + static_cast<s32>(level.topoType), static_cast<s32>(level.locType), level.protocols.size(), | ||
| 378 | + level.portNums.size()); | ||
| 379 | + candidates.push_back(std::move(level)); | ||
| 380 | + } | ||
| 381 | + | ||
| 382 | + if (!merged) { | ||
| 383 | + // 没有同范围的TopoInstance: 分区信息仍有效但拿不到链路属性, 用hasTopoInst=false显式注明 | ||
| 384 | + PhysicalLevelInfo level; | ||
| 385 | + level.localRanks = std::move(netRanks); | ||
| 386 | + level.view = PhysicalLevelView::GLOBAL; | ||
| 387 | + level.instSizeListByLayer = std::move(instSizeListByLayer); | ||
| 388 | + level.ref.netLayer = layer; | ||
| 389 | + level.ref.topoInstId = INVALID_UINT; | ||
| 390 | + level.hasTopoInst = false; | ||
| 391 | + HCCL_DEBUG( | ||
| 392 | + "[PhysicalLevel][Build] layer[%u] has no same-range topo instance, level carries partition only, " | ||
| 393 | + "rankNum[%zu]", | ||
| 394 | + layer, level.localRanks.size()); | ||
| 395 | + candidates.push_back(std::move(level)); | ||
| 396 | + } | ||
| 397 | + return HCCL_SUCCESS; | ||
| 398 | + } | ||
| 399 | + | ||
| 400 | + HcclResult BuildPhysicalLevelCandidates( | ||
| 401 | + HcclComm comm, const TopoInfoWithNetLayerDetails* topoInfo, std::vector<PhysicalLevelInfo>& candidates) | ||
| 402 | + { | ||
| 403 | + candidates.clear(); | ||
| 404 | + if (topoInfo->netLayerDetails.netLayers.empty()) { | ||
| 405 | + HCCL_WARNING("[PhysicalLevel][Build] netLayers is empty, rank[%u]", topoInfo->userRank); | ||
| 406 | + return HCCL_E_INTERNAL; | ||
| 407 | + } | ||
| 408 | + // 只遍历netLayers里的layer: 规避HCOMM对非法layer的抛异常分支, 并保证ref.netLayer都来自 | ||
| 409 | + // GetLayers的实际结果。收集先后无所谓, LevelLess是全序, 不依赖输入顺序 | ||
| 410 | + for (u32 layer : topoInfo->netLayerDetails.netLayers) { | ||
| 411 | + CHK_RET(BuildLayerCandidates(comm, topoInfo, layer, candidates)); | ||
| 412 | + } | ||
| 413 | + return HCCL_SUCCESS; | ||
| 414 | + } | ||
| 415 | + | ||
| 416 | +} // namespace | ||
| 417 | + | ||
| 418 | +HcclResult BuildPhysicalLevels(HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo) | ||
| 419 | +{ | ||
| 420 | + CHK_PTR_NULL(topoInfo); | ||
| 421 | + topoInfo->physicalLevels.clear(); | ||
| 422 | + if (comm == nullptr) { | ||
| 423 | + HCCL_WARNING("[PhysicalLevel][Build] comm is null, physicalLevels stays empty"); | ||
| 424 | + return HCCL_SUCCESS; | ||
| 425 | + } | ||
| 426 | + | ||
| 427 | + // 在临时对象中构建, 全部校验通过后再赋值; 降级或失败时physicalLevels保持为空 | ||
| 428 | + std::vector<PhysicalLevelInfo> candidates; | ||
| 429 | + std::vector<PhysicalLevelInfo> levels; | ||
| 430 | + | ||
| 431 | + HcclResult ret = BuildPhysicalLevelCandidates(comm, topoInfo, candidates); | ||
| 432 | + if (ret == HCCL_SUCCESS) { | ||
| 433 | + ret = NormalizePhysicalLevels(candidates, topoInfo->userRank, topoInfo->userRankSize, levels); | ||
| 434 | + } | ||
| 435 | + if (ret == HCCL_SUCCESS) { | ||
| 436 | + ret = ValidatePhysicalLevels(levels, topoInfo->userRank, topoInfo->userRankSize); | ||
| 437 | + } | ||
| 438 | + if (ret != HCCL_SUCCESS) { | ||
| 439 | + // 任何失败一律降级, 不改变CalcTopoShape的返回值 | ||
| 440 | + HCCL_WARNING( | ||
| 441 | + "[PhysicalLevel][Build] normalize degraded, ret[%d], rank[%u]. physicalLevels stays empty, legacy path " | ||
| 442 | + "unaffected.", | ||
| 443 | + ret, topoInfo->userRank); | ||
| 444 | + return HCCL_SUCCESS; | ||
| 445 | + } | ||
| 446 | + | ||
| 447 | + topoInfo->physicalLevels = std::move(levels); | ||
| 448 | + const size_t levelNum = topoInfo->physicalLevels.size(); | ||
| 449 | + HCCL_RUN_INFO("[PhysicalLevel][Build] rank[%u] built [%zu] physical levels", topoInfo->userRank, levelNum); | ||
| 450 | + // 最终产物逐级各打一行, 打的是真正落进topoInfo的内容(BuildLayerCandidates那条DEBUG打的是候选)。 | ||
| 451 | + // RUN_INFO让默认日志级别下就搜得到, INFO让这几行与同批INFO落在同一条时间线上 | ||
| 452 | + for (size_t idx = 0; idx < levelNum; ++idx) { | ||
| 453 | + const std::string desc = DescribeLevel(topoInfo->physicalLevels[idx], idx, levelNum); | ||
| 454 | + HCCL_RUN_INFO("[PhysicalLevel][Build] rank[%u] %s", topoInfo->userRank, desc.c_str()); | ||
| 455 | + HCCL_INFO("[PhysicalLevel][Build] rank[%u] %s", topoInfo->userRank, desc.c_str()); | ||
| 456 | + } | ||
| 457 | + return HCCL_SUCCESS; | ||
| 458 | +} | ||
| 459 | + | ||
| 460 | +} // namespace ops_hccl | ||
| @@ -0,0 +1,371 @@ | |||
| 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 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | +namespace ops_hccl { | ||
| 21 | +namespace { | ||
| 22 | + | ||
| 23 | + void SortUnique(std::vector<u32>& ranks) | ||
| 24 | + { | ||
| 25 | + std::sort(ranks.begin(), ranks.end()); | ||
| 26 | + ranks.erase(std::unique(ranks.begin(), ranks.end()), ranks.end()); | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + bool ContainsRank(const std::vector<u32>& sortedRanks, u32 rank) | ||
| 30 | + { | ||
| 31 | + return std::binary_search(sortedRanks.begin(), sortedRanks.end(), rank); | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + /** | ||
| 35 | + * topoType定序, 含义是互联紧密度递减(MESH直连, CLOS经交换)。不能用枚举值代替 —— 枚举里 | ||
| 36 | + * COMM_TOPO_CLOS=0 < COMM_TOPO_1DMESH=1, 正好相反。返回false(预期外类型)时必须整体降级。 | ||
| 37 | + */ | ||
| 38 | + bool TopoTypeOrder(CommTopo type, u32& order) | ||
| 39 | + { | ||
| 40 | + switch (type) { | ||
| 41 | + case CommTopo::COMM_TOPO_1DMESH: | ||
| 42 | + order = 0; | ||
| 43 | + return true; | ||
| 44 | + case CommTopo::COMM_TOPO_CLOS: | ||
| 45 | + order = 1; | ||
| 46 | + return true; | ||
| 47 | + default: | ||
| 48 | + return false; | ||
| 49 | + } | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + // 无TopoInstance的Level没有形态可言, 排在同键位的有形态Level之后。 | ||
| 53 | + // 取值必须与TopoTypeOrder的输出空间不重叠, 否则两类Level会在第三键上打平 | ||
| 54 | + constexpr u32 TOPO_TYPE_ORDER_NO_TOPO_INST = 2; | ||
| 55 | + | ||
| 56 | + u32 LevelTopoOrder(const PhysicalLevelInfo& level) | ||
| 57 | + { | ||
| 58 | + u32 order = TOPO_TYPE_ORDER_NO_TOPO_INST; | ||
| 59 | + if (level.hasTopoInst) { | ||
| 60 | + // 排序前已逐个校验过topoType可定序, 此处必然成功 | ||
| 61 | + (void)TopoTypeOrder(level.topoType, order); | ||
| 62 | + } | ||
| 63 | + return order; | ||
| 64 | + } | ||
| 65 | + | ||
| 66 | + // 排序三键 + 两个确定性兜底键, 语义见physical_levels_design.md | ||
| 67 | + bool LevelLess(const PhysicalLevelInfo& lhs, const PhysicalLevelInfo& rhs) | ||
| 68 | + { | ||
| 69 | + // 键1: 当前rank在该级的块大小。GLOBAL与LOCAL级的localRanks同量纲, 可直接比较 | ||
| 70 | + if (lhs.localRanks.size() != rhs.localRanks.size()) { | ||
| 71 | + return lhs.localRanks.size() < rhs.localRanks.size(); | ||
| 72 | + } | ||
| 73 | + // 键2: LOCAL(0)在GLOBAL(1)之前 | ||
| 74 | + if (lhs.view != rhs.view) { | ||
| 75 | + return lhs.view < rhs.view; | ||
| 76 | + } | ||
| 77 | + // 键3: netLayer 0同时挂MESH和CLOS且rank集合相同时, 前两键全部打平, 定序完全依赖这一键 | ||
| 78 | + const u32 lhsOrder = LevelTopoOrder(lhs); | ||
| 79 | + const u32 rhsOrder = LevelTopoOrder(rhs); | ||
| 80 | + if (lhsOrder != rhsOrder) { | ||
| 81 | + return lhsOrder < rhsOrder; | ||
| 82 | + } | ||
| 83 | + // 兜底键1, 正常输入上永不决定顺序: 此时两级必然互相重叠且互不包含, 会在链校验中被拒。 | ||
| 84 | + // 保留它只为让"被拒"这件事本身也是确定的, 不随RankGraph的哈希返回序抖动 | ||
| 85 | + if (lhs.localRanks != rhs.localRanks) { | ||
| 86 | + return lhs.localRanks < rhs.localRanks; | ||
| 87 | + } | ||
| 88 | + // 兜底键2: 原始身份。前面所有键都相同仍可能是两个不同Level(如两个netLayer的本地 | ||
| 89 | + // NetInstance恰好同范围)。少了这一键它们在比较器下等价, std::sort的相对顺序未指定, | ||
| 90 | + // 各rank排出的下标语义会分叉; netLayer与topoInstId跨rank一致, 补上后比较器成为全序 | ||
| 91 | + if (lhs.ref.netLayer != rhs.ref.netLayer) { | ||
| 92 | + return lhs.ref.netLayer < rhs.ref.netLayer; | ||
| 93 | + } | ||
| 94 | + return lhs.ref.topoInstId < rhs.ref.topoInstId; | ||
| 95 | + } | ||
| 96 | + | ||
| 97 | + bool IsStrictlyAscending(const std::vector<u32>& ranks) | ||
| 98 | + { | ||
| 99 | + for (size_t idx = 1; idx < ranks.size(); idx++) { | ||
| 100 | + if (ranks[idx] <= ranks[idx - 1]) { | ||
| 101 | + return false; | ||
| 102 | + } | ||
| 103 | + } | ||
| 104 | + return true; | ||
| 105 | + } | ||
| 106 | + | ||
| 107 | + bool IsSuperSetOf(const std::vector<u32>& outer, const std::vector<u32>& inner) | ||
| 108 | + { | ||
| 109 | + return std::includes(outer.begin(), outer.end(), inner.begin(), inner.end()); | ||
| 110 | + } | ||
| 111 | + | ||
| 112 | +} // namespace | ||
| 113 | + | ||
| 114 | +bool EndpointDescLess(const EndpointDesc& lhs, const EndpointDesc& rhs) | ||
| 115 | +{ | ||
| 116 | + if (lhs.protocol != rhs.protocol) { | ||
| 117 | + return lhs.protocol < rhs.protocol; | ||
| 118 | + } | ||
| 119 | + if (lhs.loc.locType != rhs.loc.locType) { | ||
| 120 | + return lhs.loc.locType < rhs.loc.locType; | ||
| 121 | + } | ||
| 122 | + if (lhs.commAddr.type != rhs.commAddr.type) { | ||
| 123 | + return lhs.commAddr.type < rhs.commAddr.type; | ||
| 124 | + } | ||
| 125 | + return memcmp(lhs.commAddr.raws, rhs.commAddr.raws, sizeof(lhs.commAddr.raws)) < 0; | ||
| 126 | +} | ||
| 127 | + | ||
| 128 | +bool CommAddrEqual(const CommAddr& lhs, const CommAddr& rhs) | ||
| 129 | +{ | ||
| 130 | + return lhs.type == rhs.type && memcmp(lhs.raws, rhs.raws, sizeof(lhs.raws)) == 0; | ||
| 131 | +} | ||
| 132 | + | ||
| 133 | +HcclResult NormalizePhysicalLevels( | ||
| 134 | + std::vector<PhysicalLevelInfo>& candidates, u32 userRank, u32 userRankSize, std::vector<PhysicalLevelInfo>& levels) | ||
| 135 | +{ | ||
| 136 | + levels.clear(); | ||
| 137 | + if (userRankSize == 0 || userRank >= userRankSize) { | ||
| 138 | + HCCL_WARNING( | ||
| 139 | + "[PhysicalLevel][Normalize] invalid rank info, userRank[%u], userRankSize[%u]", userRank, userRankSize); | ||
| 140 | + return HCCL_E_NOT_SUPPORT; | ||
| 141 | + } | ||
| 142 | + | ||
| 143 | + // 1. 归一: rank列表排序去重、portNums降序、protocols去重升序, 剔除不含当前rank的候选, | ||
| 144 | + // 并确认topoType可定序(排序第三键的前提)。instSizeListByLayer不参与归一 —— 重排会毁掉布局语义。 | ||
| 145 | + // 构建侧已做过同样的规范化, 这一步在正常路径上幂等, 保留是为了让本函数可离线UT | ||
| 146 | + std::vector<PhysicalLevelInfo> validCands; | ||
| 147 | + validCands.reserve(candidates.size()); | ||
| 148 | + for (auto& cand : candidates) { | ||
| 149 | + SortUnique(cand.localRanks); | ||
| 150 | + // 不能去重: 两条8口链路就是{8,8}, 求和才是总端口数。去重的是iface, 不是端口数值 | ||
| 151 | + std::sort(cand.portNums.begin(), cand.portNums.end(), std::greater<u32>()); | ||
| 152 | + std::sort(cand.protocols.begin(), cand.protocols.end()); | ||
| 153 | + cand.protocols.erase(std::unique(cand.protocols.begin(), cand.protocols.end()), cand.protocols.end()); | ||
| 154 | + if (!ContainsRank(cand.localRanks, userRank)) { | ||
| 155 | + HCCL_DEBUG( | ||
| 156 | + "[PhysicalLevel][Normalize] drop candidate without myRank[%u], rankNum[%zu]", userRank, | ||
| 157 | + cand.localRanks.size()); | ||
| 158 | + continue; | ||
| 159 | + } | ||
| 160 | + u32 unusedOrder = 0; | ||
| 161 | + if (cand.hasTopoInst && !TopoTypeOrder(cand.topoType, unusedOrder)) { | ||
| 162 | + HCCL_WARNING( | ||
| 163 | + "[PhysicalLevel][Normalize] level at layer[%u] inst[%u] has unorderable topoType[%d], rank[%u]", | ||
| 164 | + cand.ref.netLayer, cand.ref.topoInstId, static_cast<s32>(cand.topoType), userRank); | ||
| 165 | + return HCCL_E_NOT_SUPPORT; | ||
| 166 | + } | ||
| 167 | + validCands.push_back(std::move(cand)); | ||
| 168 | + } | ||
| 169 | + if (validCands.empty()) { | ||
| 170 | + HCCL_WARNING("[PhysicalLevel][Normalize] no valid candidate for rank[%u]", userRank); | ||
| 171 | + return HCCL_E_NOT_SUPPORT; | ||
| 172 | + } | ||
| 173 | + | ||
| 174 | + // 2. 三键排序, 不做合并(Level与NetInstance/TopoInstance一一对应)。用sort而非stable_sort: | ||
| 175 | + // LevelLess是全序, 结果与输入顺序无关 —— 输入顺序来自RankGraph的哈希遍历, 本就不可依赖 | ||
| 176 | + std::sort(validCands.begin(), validCands.end(), LevelLess); | ||
| 177 | + levels = std::move(validCands); | ||
| 178 | + | ||
| 179 | + // 3. 链校验: 相邻范围必须满足包含关系, 允许相等。 | ||
| 180 | + // 互相重叠但互不包含的范围(典型为2D Mesh的x/y环)在此被拒绝 | ||
| 181 | + for (size_t idx = 1; idx < levels.size(); idx++) { | ||
| 182 | + if (!IsSuperSetOf(levels[idx].localRanks, levels[idx - 1].localRanks)) { | ||
| 183 | + HCCL_WARNING( | ||
| 184 | + "[PhysicalLevel][Normalize] level[%zu] with rankNum[%zu] does not contain level[%zu] with " | ||
| 185 | + "rankNum[%zu], ranges do not form a chain, rank[%u]", | ||
| 186 | + idx, levels[idx].localRanks.size(), idx - 1, levels[idx - 1].localRanks.size(), userRank); | ||
| 187 | + levels.clear(); | ||
| 188 | + return HCCL_E_NOT_SUPPORT; | ||
| 189 | + } | ||
| 190 | + } | ||
| 191 | + | ||
| 192 | + HCCL_INFO("[PhysicalLevel][Normalize] rank[%u] got [%zu] levels", userRank, levels.size()); | ||
| 193 | + return HCCL_SUCCESS; | ||
| 194 | +} | ||
| 195 | + | ||
| 196 | +HcclResult ValidatePhysicalLevels(const std::vector<PhysicalLevelInfo>& levels, u32 userRank, u32 userRankSize) | ||
| 197 | +{ | ||
| 198 | + // 不变量1 | ||
| 199 | + if (userRankSize == 0 || userRank >= userRankSize) { | ||
| 200 | + HCCL_WARNING( | ||
| 201 | + "[PhysicalLevel][Validate] invalid rank info, userRank[%u], userRankSize[%u]", userRank, userRankSize); | ||
| 202 | + return HCCL_E_NOT_SUPPORT; | ||
| 203 | + } | ||
| 204 | + if (levels.empty()) { | ||
| 205 | + HCCL_WARNING("[PhysicalLevel][Validate] levels is empty, rank[%u]", userRank); | ||
| 206 | + return HCCL_E_NOT_SUPPORT; | ||
| 207 | + } | ||
| 208 | + | ||
| 209 | + for (size_t idx = 0; idx < levels.size(); idx++) { | ||
| 210 | + const PhysicalLevelInfo& level = levels[idx]; | ||
| 211 | + | ||
| 212 | + // 不变量2: 非空、升序严格递增(等价于无重复)、无越界、含当前rank | ||
| 213 | + if (level.localRanks.empty() || !IsStrictlyAscending(level.localRanks)) { | ||
| 214 | + HCCL_WARNING("[PhysicalLevel][Validate] level[%zu] rank list is empty or not ascending", idx); | ||
| 215 | + return HCCL_E_NOT_SUPPORT; | ||
| 216 | + } | ||
| 217 | + if (level.localRanks.back() >= userRankSize) { | ||
| 218 | + HCCL_WARNING( | ||
| 219 | + "[PhysicalLevel][Validate] level[%zu] max rank[%u] exceeds userRankSize[%u]", idx, | ||
| 220 | + level.localRanks.back(), userRankSize); | ||
| 221 | + return HCCL_E_NOT_SUPPORT; | ||
| 222 | + } | ||
| 223 | + if (!ContainsRank(level.localRanks, userRank)) { | ||
| 224 | + HCCL_WARNING("[PhysicalLevel][Validate] level[%zu] does not contain myRank[%u]", idx, userRank); | ||
| 225 | + return HCCL_E_NOT_SUPPORT; | ||
| 226 | + } | ||
| 227 | + | ||
| 228 | + // 不变量3a: view必须是有效枚举值。底层类型是u32, 不白名单则非法值会静默落进else被当成GLOBAL | ||
| 229 | + if (level.view != PhysicalLevelView::LOCAL && level.view != PhysicalLevelView::GLOBAL) { | ||
| 230 | + HCCL_WARNING( | ||
| 231 | + "[PhysicalLevel][Validate] level[%zu] has invalid view[%u]", idx, static_cast<u32>(level.view)); | ||
| 232 | + return HCCL_E_NOT_SUPPORT; | ||
| 233 | + } | ||
| 234 | + | ||
| 235 | + // 不变量3b: ref.netLayer恒有效。无效值说明构建侧漏填, 消费侧回查时会拿到错误的层 | ||
| 236 | + if (level.ref.netLayer == INVALID_UINT) { | ||
| 237 | + HCCL_WARNING("[PhysicalLevel][Validate] level[%zu] has no valid netLayer", idx); | ||
| 238 | + return HCCL_E_NOT_SUPPORT; | ||
| 239 | + } | ||
| 240 | + | ||
| 241 | + // 不变量3c: hasTopoInst与链路属性必须自洽, 否则消费侧会把一堆无效值当成真实链路事实建模 | ||
| 242 | + if (level.hasTopoInst) { | ||
| 243 | + if (level.ref.topoInstId == INVALID_UINT) { | ||
| 244 | + HCCL_WARNING("[PhysicalLevel][Validate] level[%zu] hasTopoInst but topoInstId is invalid", idx); | ||
| 245 | + return HCCL_E_NOT_SUPPORT; | ||
| 246 | + } | ||
| 247 | + // portNums按iface去重, 条数不会超过endpoint数; 超过说明去重逻辑坏了, 总端口数会被算大 | ||
| 248 | + if (level.portNums.size() > level.endpoints.size()) { | ||
| 249 | + HCCL_WARNING( | ||
| 250 | + "[PhysicalLevel][Validate] level[%zu] portNum count[%zu] exceeds endpoint count[%zu]", idx, | ||
| 251 | + level.portNums.size(), level.endpoints.size()); | ||
| 252 | + return HCCL_E_NOT_SUPPORT; | ||
| 253 | + } | ||
| 254 | + // 0能完整穿过下面的降序检查(排在末尾), 于是不存在的链路会被当成真实出口计入 | ||
| 255 | + for (u32 portNum : level.portNums) { | ||
| 256 | + if (portNum == 0 || portNum > PORT_NUM_SANITY_LIMIT) { | ||
| 257 | + HCCL_WARNING("[PhysicalLevel][Validate] level[%zu] has implausible portNum[%u]", idx, portNum); | ||
| 258 | + return HCCL_E_NOT_SUPPORT; | ||
| 259 | + } | ||
| 260 | + } | ||
| 261 | + // 降序规范化: 采集顺序来自endpoints的哈希序, 不规范化则跨进程字节流不同 | ||
| 262 | + if (!std::is_sorted(level.portNums.begin(), level.portNums.end(), std::greater<u32>())) { | ||
| 263 | + HCCL_WARNING("[PhysicalLevel][Validate] level[%zu] portNums is not sorted descending", idx); | ||
| 264 | + return HCCL_E_NOT_SUPPORT; | ||
| 265 | + } | ||
| 266 | + // protocols去重升序, 理由同上 | ||
| 267 | + if (!std::is_sorted(level.protocols.begin(), level.protocols.end()) | ||
| 268 | + || std::adjacent_find(level.protocols.begin(), level.protocols.end()) != level.protocols.end()) { | ||
| 269 | + HCCL_WARNING("[PhysicalLevel][Validate] level[%zu] protocols is not sorted and unique", idx); | ||
| 270 | + return HCCL_E_NOT_SUPPORT; | ||
| 271 | + } | ||
| 272 | + } else { | ||
| 273 | + // 无TopoInstance: 全部链路属性必须保持无效值。"半有"状态最危险 —— 消费侧判定为不可用, | ||
| 274 | + // 却又能从字段里读出看似合理的值 | ||
| 275 | + if (level.ref.topoInstId != INVALID_UINT || level.topoType != CommTopo::COMM_TOPO_RESERVED | ||
| 276 | + || level.locType != EndpointLocType::ENDPOINT_LOC_TYPE_RESERVED || !level.protocols.empty() | ||
| 277 | + || !level.portNums.empty() || !level.endpoints.empty()) { | ||
| 278 | + HCCL_WARNING( | ||
| 279 | + "[PhysicalLevel][Validate] level[%zu] has no topo instance but carries link attributes", idx); | ||
| 280 | + return HCCL_E_NOT_SUPPORT; | ||
| 281 | + } | ||
| 282 | + } | ||
| 283 | + | ||
| 284 | + // 不变量3d: view与instSizeListByLayer是否为空严格等价; 错开之后消费侧会把只知道本块的级 | ||
| 285 | + // 当成全局分区来切算法 | ||
| 286 | + if (level.view == PhysicalLevelView::LOCAL) { | ||
| 287 | + if (!level.instSizeListByLayer.empty()) { | ||
| 288 | + HCCL_WARNING("[PhysicalLevel][Validate] level[%zu] is LOCAL but carries partition sizes", idx); | ||
| 289 | + return HCCL_E_NOT_SUPPORT; | ||
| 290 | + } | ||
| 291 | + } else { | ||
| 292 | + // 不变量4: GLOBAL级的instSizeListByLayer是该netLayer对整个通信域的一次完整划分, | ||
| 293 | + // 其和恒等于userRankSize | ||
| 294 | + const u32 total = std::accumulate(level.instSizeListByLayer.begin(), level.instSizeListByLayer.end(), 0U); | ||
| 295 | + if (level.instSizeListByLayer.empty() || total != userRankSize) { | ||
| 296 | + HCCL_WARNING( | ||
| 297 | + "[PhysicalLevel][Validate] level[%zu] instSizeListByLayer sum[%u] mismatches userRankSize[%u]", idx, | ||
| 298 | + total, userRankSize); | ||
| 299 | + return HCCL_E_NOT_SUPPORT; | ||
| 300 | + } | ||
| 301 | + // 不变量4b: 每个分区非空。0能完整穿过本块其余检查, 于是幽灵空分区会被当成真实Instance计入 | ||
| 302 | + if (std::find(level.instSizeListByLayer.begin(), level.instSizeListByLayer.end(), 0U) | ||
| 303 | + != level.instSizeListByLayer.end()) { | ||
| 304 | + HCCL_WARNING("[PhysicalLevel][Validate] level[%zu] instSizeListByLayer contains a zero entry", idx); | ||
| 305 | + return HCCL_E_NOT_SUPPORT; | ||
| 306 | + } | ||
| 307 | + // 不变量5: 当前rank的块大小必须是这一层的某个真实分区。逻辑上被不变量6蕴含, 单列是为了 | ||
| 308 | + // 把两类失败分开: 本条不过是"大小根本不存在", 只有本条过、6不过才是布局假设出了问题 | ||
| 309 | + if (std::find( | ||
| 310 | + level.instSizeListByLayer.begin(), level.instSizeListByLayer.end(), | ||
| 311 | + static_cast<u32>(level.localRanks.size())) | ||
| 312 | + == level.instSizeListByLayer.end()) { | ||
| 313 | + HCCL_WARNING( | ||
| 314 | + "[PhysicalLevel][Validate] level[%zu] localRankNum[%zu] is not one of the inst sizes", idx, | ||
| 315 | + level.localRanks.size()); | ||
| 316 | + return HCCL_E_NOT_SUPPORT; | ||
| 317 | + } | ||
| 318 | + // 不变量6: 布局自检。用userRank做前缀和必然落进某一块, 其大小必须等于localRanks.size()。 | ||
| 319 | + // 这是instSizeListByLayer唯一一处能在本地验证的跨rank性质 | ||
| 320 | + u32 cumulative = 0; | ||
| 321 | + bool located = false; | ||
| 322 | + for (u32 instSize : level.instSizeListByLayer) { | ||
| 323 | + cumulative += instSize; | ||
| 324 | + if (userRank < cumulative) { | ||
| 325 | + located = (instSize == static_cast<u32>(level.localRanks.size())); | ||
| 326 | + break; | ||
| 327 | + } | ||
| 328 | + } | ||
| 329 | + if (!located) { | ||
| 330 | + HCCL_WARNING( | ||
| 331 | + "[PhysicalLevel][Validate] level[%zu] rank[%u] cannot be located in instSizeListByLayer " | ||
| 332 | + "with a block of localRankNum[%zu]", | ||
| 333 | + idx, userRank, level.localRanks.size()); | ||
| 334 | + return HCCL_E_NOT_SUPPORT; | ||
| 335 | + } | ||
| 336 | + } | ||
| 337 | + | ||
| 338 | + // 不变量7: 大小非递减 + 包含链。允许相等 —— netLayer 0上同范围的MESH与CLOS两级、 | ||
| 339 | + // 两个netLayer的本地NetInstance恰好同范围, 都是合法的相等相邻对 | ||
| 340 | + if (idx > 0) { | ||
| 341 | + if (level.localRanks.size() < levels[idx - 1].localRanks.size()) { | ||
| 342 | + HCCL_WARNING( | ||
| 343 | + "[PhysicalLevel][Validate] level[%zu] rankNum[%zu] is less than level[%zu] rankNum[%zu]", idx, | ||
| 344 | + level.localRanks.size(), idx - 1, levels[idx - 1].localRanks.size()); | ||
| 345 | + return HCCL_E_NOT_SUPPORT; | ||
| 346 | + } | ||
| 347 | + if (!IsSuperSetOf(level.localRanks, levels[idx - 1].localRanks)) { | ||
| 348 | + HCCL_WARNING("[PhysicalLevel][Validate] level[%zu] does not contain level[%zu]", idx, idx - 1); | ||
| 349 | + return HCCL_E_NOT_SUPPORT; | ||
| 350 | + } | ||
| 351 | + } | ||
| 352 | + } | ||
| 353 | + | ||
| 354 | + // 不变量8: 身份(netLayer, topoInstId)全域唯一。LevelLess的兜底键正是靠这两项才构成全序, | ||
| 355 | + // 重复则两个Level在比较器下等价, std::sort的相对顺序未指定, 各rank的下标语义会分叉。 | ||
| 356 | + // levels规模是个位数(上限PHYSICAL_LEVEL_NUM_LIMIT), 两两比较不需要额外容器 | ||
| 357 | + for (size_t i = 0; i < levels.size(); i++) { | ||
| 358 | + for (size_t j = i + 1; j < levels.size(); j++) { | ||
| 359 | + if (levels[i].ref.netLayer == levels[j].ref.netLayer | ||
| 360 | + && levels[i].ref.topoInstId == levels[j].ref.topoInstId) { | ||
| 361 | + HCCL_WARNING( | ||
| 362 | + "[PhysicalLevel][Validate] level[%zu] and level[%zu] share the same source: layer[%u] inst[%u]", i, | ||
| 363 | + j, levels[i].ref.netLayer, levels[i].ref.topoInstId); | ||
| 364 | + return HCCL_E_NOT_SUPPORT; | ||
| 365 | + } | ||
| 366 | + } | ||
| 367 | + } | ||
| 368 | + return HCCL_SUCCESS; | ||
| 369 | +} | ||
| 370 | + | ||
| 371 | +} // namespace ops_hccl | ||
| @@ -23,6 +23,7 @@ | |||
| 23 | 23 | ||
| 24 | 24 | ||
| 25 | 25 | ||
| 26 | + | ||
| 26 | 27 | ||
| 27 | constexpr u32 FACTOR_NUM_TWO = 2; | 28 | constexpr u32 FACTOR_NUM_TWO = 2; |
| 28 | constexpr s32 DEVICE_PER_MODULE = 8; | 29 | constexpr s32 DEVICE_PER_MODULE = 8; |
| @@ -769,6 +770,62 @@ static HcclResult CalcLevel2Ubg(const HcclComm comm, TopoInfoWithNetLayerDetails | |||
| 769 | return HCCL_SUCCESS; | 770 | return HCCL_SUCCESS; |
| 770 | } | 771 | } |
| 771 | 772 | ||
| 773 | +/** | ||
| 774 | + * 查询本卡是否为POD机型。恒返回HCCL_SUCCESS: 该字段是纯附加信息, 取不到时停在false, | ||
| 775 | + * 现有字段与旧执行路径完全不受影响。设备号的三套口径与转换理由见physical_levels_design.md。 | ||
| 776 | + */ | ||
| 777 | +HcclResult CalcDeviceFormFactor(TopoInfoWithNetLayerDetails* topoInfo) | ||
| 778 | +{ | ||
| 779 | + CHK_PTR_NULL(topoInfo); | ||
| 780 | + topoInfo->isPod = false; | ||
| 781 | + | ||
| 782 | + | ||
| 783 | + // 老CANN的acl_rt.h没有ACL_DEV_ATTR_DEVICE_FORM_FACTOR, 无从查起, 一律按非POD建模。 | ||
| 784 | + // 与成功路径同为INFO同前缀: 现场grep一次就能分清走的是哪一条 | ||
| 785 | + HCCL_INFO("[Topo][CalcDeviceFormFactor] acl has no device form factor attr, isPod stays false"); | ||
| 786 | + | ||
| 787 | + // aclrtGetDevice返回的是userDevId, 不是logicDevId, 变量名必须如实反映 | ||
| 788 | + s32 userDevId = 0; | ||
| 789 | + aclError aclRet = aclrtGetDevice(&userDevId); | ||
| 790 | + if (aclRet != ACL_SUCCESS) { | ||
| 791 | + HCCL_WARNING("[Topo][CalcDeviceFormFactor] get current device failed, ret[%d]. isPod stays false.", aclRet); | ||
| 792 | + return HCCL_SUCCESS; | ||
| 793 | + } | ||
| 794 | + | ||
| 795 | + // userDevId -> logicDevId。aclrtGetDeviceInfo要的是logicDevId, 少这一步在配了 | ||
| 796 | + // ASCEND_RT_VISIBLE_DEVICES的环境上会静默读到另一张卡的形态 | ||
| 797 | + s32 logicDevId = 0; | ||
| 798 | + aclRet = aclrtGetLogicDevIdByUserDevId(userDevId, &logicDevId); | ||
| 799 | + if (aclRet != ACL_SUCCESS) { | ||
| 800 | + HCCL_WARNING( | ||
| 801 | + "[Topo][CalcDeviceFormFactor] get logic dev id by user dev id[%d] failed, ret[%d]. " | ||
| 802 | + "isPod stays false.", | ||
| 803 | + userDevId, aclRet); | ||
| 804 | + return HCCL_SUCCESS; | ||
| 805 | + } | ||
| 806 | + | ||
| 807 | + s64 val = 0; | ||
| 808 | + // quiet: 老驱动不支持该infoType时会稳定失败, 按ERROR打会在正常的老环境上持续刷错误日志 | ||
| 809 | + HcclResult ret = hcalrtGetDeviceInfo(static_cast<u32>(logicDevId), ACL_DEV_ATTR_DEVICE_FORM_FACTOR, val, true); | ||
| 810 | + if (ret != HCCL_SUCCESS) { | ||
| 811 | + HCCL_WARNING( | ||
| 812 | + "[Topo][CalcDeviceFormFactor] get device form factor failed, ret[%d], logicDevId[%d]. " | ||
| 813 | + "isPod stays false.", | ||
| 814 | + ret, logicDevId); | ||
| 815 | + return HCCL_SUCCESS; | ||
| 816 | + } | ||
| 817 | + | ||
| 818 | + // 必须是严格相等的正向判断: ACL将来新增形态时, 未识别的取值必须落到false一侧 | ||
| 819 | + topoInfo->isPod = (val == ACL_DEVICE_FORM_FACTOR_POD); | ||
| 820 | + // 原始取值与两个设备号都打出来: 现场据此分辨"确实不是POD"还是"取到了个没见过的形态" | ||
| 821 | + HCCL_INFO( | ||
| 822 | + "[Topo][CalcDeviceFormFactor] userDevId[%d] logicDevId[%d] formFactor[%ld] isPod[%d]", userDevId, logicDevId, | ||
| 823 | + val, static_cast<s32>(topoInfo->isPod)); | ||
| 824 | + | ||
| 825 | + | ||
| 826 | + return HCCL_SUCCESS; | ||
| 827 | +} | ||
| 828 | + | ||
| 772 | HcclResult CalcTopoShape(HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo) | 829 | HcclResult CalcTopoShape(HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo) |
| 773 | { | 830 | { |
| 774 | CHK_RET(ExtractNetLayerDetails(comm, topoInfo)); | 831 | CHK_RET(ExtractNetLayerDetails(comm, topoInfo)); |
| @@ -781,6 +838,11 @@ HcclResult CalcTopoShape(HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo) | |||
| 781 | CHK_RET(CalcLevel2Uboe(comm, topoInfo)); | 838 | CHK_RET(CalcLevel2Uboe(comm, topoInfo)); |
| 782 | CHK_RET(CalcLevel2Ubg(comm, topoInfo)); | 839 | CHK_RET(CalcLevel2Ubg(comm, topoInfo)); |
| 783 | CHK_RET(CalcHostDPUOnly(comm, topoInfo)); | 840 | CHK_RET(CalcHostDPUOnly(comm, topoInfo)); |
| 841 | + // 与comm无关, 只查本卡; 恒返回HCCL_SUCCESS, 取不到时停在false | ||
| 842 | + CHK_RET(CalcDeviceFormFactor(topoInfo)); | ||
| 843 | + // 放在最后: 现有字段的提取与派生逻辑完全不受影响, 且可复用已提取的netLayerDetails。 | ||
| 844 | + // BuildPhysicalLevels恒返回HCCL_SUCCESS, 内部失败一律降级为空视图 | ||
| 845 | + CHK_RET(BuildPhysicalLevels(comm, topoInfo)); | ||
| 784 | return HCCL_SUCCESS; | 846 | return HCCL_SUCCESS; |
| 785 | } | 847 | } |
| 786 | 848 | ||
| @@ -94,6 +94,12 @@ HcclResult CalcTopoShape(HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo); | |||
| 94 | 94 | ||
| 95 | HcclResult CalcHostDPUOnly(HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo); | 95 | HcclResult CalcHostDPUOnly(HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo); |
| 96 | 96 | ||
| 97 | +/** | ||
| 98 | + * 查询本卡是否为POD机型并写入topoInfo->isPod。不依赖HcclComm, 只查本设备。 | ||
| 99 | + * 恒返回HCCL_SUCCESS: 取不到时停在false, 不影响任何现有字段与执行路径。 | ||
| 100 | + */ | ||
| 101 | +HcclResult CalcDeviceFormFactor(TopoInfoWithNetLayerDetails* topoInfo); | ||
| 102 | + | ||
| 97 | HcclResult ExtractNetLayerDetails(const HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo); | 103 | HcclResult ExtractNetLayerDetails(const HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo); |
| 98 | 104 | ||
| 99 | HcclResult ExtractTopoDetails(HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo); | 105 | HcclResult ExtractTopoDetails(HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo); |
| @@ -0,0 +1,354 @@ | |||
| 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 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | +namespace ops_hccl { | ||
| 15 | + | ||
| 16 | +TopoMatchBaseV2::TopoMatchBaseV2() {} | ||
| 17 | +TopoMatchBaseV2::~TopoMatchBaseV2() {} | ||
| 18 | + | ||
| 19 | +u32 CalcGcdByPair(u32 a, u32 b) | ||
| 20 | +{ | ||
| 21 | + if (a == 0 || b == 0) { | ||
| 22 | + return 1; | ||
| 23 | + } | ||
| 24 | + while (b != 0) { | ||
| 25 | + u32 r = a % b; | ||
| 26 | + a = b; | ||
| 27 | + b = r; | ||
| 28 | + } | ||
| 29 | + HCCL_DEBUG("[CalcGcdByPair] a[%u] b[%u], gcd[%u]", a, b, a); | ||
| 30 | + return a; | ||
| 31 | +} | ||
| 32 | + | ||
| 33 | +u32 CalcGcd(const std::vector<u32>& nums) | ||
| 34 | +{ | ||
| 35 | + if (nums.empty()) { | ||
| 36 | + return 1; | ||
| 37 | + } | ||
| 38 | + u32 result = nums[0]; | ||
| 39 | + for (size_t i = 1; i < nums.size(); i++) { | ||
| 40 | + result = CalcGcdByPair(result, nums[i]); | ||
| 41 | + if (result == 1) { | ||
| 42 | + return 1; | ||
| 43 | + } | ||
| 44 | + } | ||
| 45 | + HCCL_DEBUG("[CalcGcd] size[%u], gcd[%u]", static_cast<u32>(nums.size()), result); | ||
| 46 | + return result; | ||
| 47 | +} | ||
| 48 | + | ||
| 49 | +int32_t FindHighestEffectiveLevel(const std::vector<PhysicalLevelInfo>& physicalLevels) | ||
| 50 | +{ | ||
| 51 | + for (int32_t i = static_cast<int32_t>(physicalLevels.size()) - 1; i >= 0; i--) { | ||
| 52 | + if (physicalLevels[i].hasTopoInst) { | ||
| 53 | + return i; | ||
| 54 | + } | ||
| 55 | + } | ||
| 56 | + return INVALID_PHYSICAL_LEVEL_IDX; | ||
| 57 | +} | ||
| 58 | + | ||
| 59 | +bool IsInstListSymmetric(const std::vector<uint32_t>& instList) | ||
| 60 | +{ | ||
| 61 | + if (instList.empty()) { | ||
| 62 | + HCCL_WARNING("[TopoMatchBase] instList is empty!"); | ||
| 63 | + return true; | ||
| 64 | + } | ||
| 65 | + for (size_t i = 0; i < instList.size(); i++) { | ||
| 66 | + if (instList[i] != instList[0]) { | ||
| 67 | + return false; | ||
| 68 | + } | ||
| 69 | + } | ||
| 70 | + return true; | ||
| 71 | +} | ||
| 72 | + | ||
| 73 | +std::vector<u32> BuildRepresentativeGroup(u32 step, u32 count, u32 offset) | ||
| 74 | +{ | ||
| 75 | + std::vector<u32> group; | ||
| 76 | + group.reserve(count); | ||
| 77 | + for (u32 i = 0; i < count; i++) { | ||
| 78 | + group.push_back(offset + i * step); | ||
| 79 | + } | ||
| 80 | + return group; | ||
| 81 | +} | ||
| 82 | + | ||
| 83 | +HcclResult ValidateGroup(const std::vector<u32>& group, u32 dim, u32 myRank, const std::string& levelName) | ||
| 84 | +{ | ||
| 85 | + if (group.size() != dim) { | ||
| 86 | + HCCL_ERROR("[TopoMatchBase] Rank [%u], %s group size[%zu] != dim[%u].", myRank, levelName.c_str(), | ||
| 87 | + group.size(), dim); | ||
| 88 | + return HcclResult::HCCL_E_INTERNAL; | ||
| 89 | + } | ||
| 90 | + if (std::find(group.begin(), group.end(), myRank) == group.end()) { | ||
| 91 | + HCCL_ERROR("[TopoMatchBase] Rank [%u], %s group does not contain myRank.", myRank, levelName.c_str()); | ||
| 92 | + return HcclResult::HCCL_E_INTERNAL; | ||
| 93 | + } | ||
| 94 | + return HcclResult::HCCL_SUCCESS; | ||
| 95 | +} | ||
| 96 | + | ||
| 97 | +// 判断 protocols 是否含 UBG 链路(AIV 引擎需排除这种层) | ||
| 98 | +static bool HasUbgLink(const std::vector<CommProtocol>& protocols) | ||
| 99 | +{ | ||
| 100 | + for (CommProtocol p : protocols) { | ||
| 101 | + if (p == COMM_PROTOCOL_UBG) { | ||
| 102 | + return true; | ||
| 103 | + } | ||
| 104 | + } | ||
| 105 | + return false; | ||
| 106 | +} | ||
| 107 | + | ||
| 108 | +// 按引擎过滤收集 hasTopoInst 的物理层序号:非 hostdpu 排除 HOST 层,AIV 排除含 UBG 链路的层 | ||
| 109 | +std::vector<u32> CollectEffectiveIndices(const std::vector<PhysicalLevelInfo>& physicalLevels, | ||
| 110 | + OpExecuteConfig engine) | ||
| 111 | +{ | ||
| 112 | + bool isHostdpu = (engine == OpExecuteConfig::HOSTCPU); | ||
| 113 | + bool isAiv = (engine == OpExecuteConfig::AIV); | ||
| 114 | + std::vector<u32> effIdx; | ||
| 115 | + for (u32 i = 0; i < physicalLevels.size(); i++) { | ||
| 116 | + if (!physicalLevels[i].hasTopoInst) { | ||
| 117 | + continue; | ||
| 118 | + } | ||
| 119 | + if (!isHostdpu && physicalLevels[i].locType == EndpointLocType::ENDPOINT_LOC_TYPE_HOST) { | ||
| 120 | + continue; | ||
| 121 | + } | ||
| 122 | + if (isAiv && HasUbgLink(physicalLevels[i].protocols)) { | ||
| 123 | + continue; | ||
| 124 | + } | ||
| 125 | + effIdx.push_back(i); | ||
| 126 | + } | ||
| 127 | + return effIdx; | ||
| 128 | +} | ||
| 129 | + | ||
| 130 | +// 判断算法是否属于 Mesh 类 | ||
| 131 | +bool IsMeshAlgo(AlgoType algo) | ||
| 132 | +{ | ||
| 133 | + return MESH_ALGO_TYPES.count(algo) > 0; | ||
| 134 | +} | ||
| 135 | + | ||
| 136 | +// 判断算法是否属于 MeshConcur 类(触发 CLOS 双层规则) | ||
| 137 | +bool IsMeshConcurAlgo(AlgoType algo) | ||
| 138 | +{ | ||
| 139 | + return MESH_CONCUR_ALGO_TYPES.count(algo) > 0; | ||
| 140 | +} | ||
| 141 | + | ||
| 142 | +// 段内匹配:算法 [algoLow..algoHigh] ↔ 物理 [physLow..physHigh],低层一一 + 最高层压缩多余 | ||
| 143 | +static void MatchLayerIdxBySegment(u32 algoLow, u32 algoHigh, u32 physLow, u32 physHigh, | ||
| 144 | + std::vector<u32>& pIndices) | ||
| 145 | +{ | ||
| 146 | + if (algoLow > algoHigh) { | ||
| 147 | + return; | ||
| 148 | + } | ||
| 149 | + u32 algoCount = algoHigh - algoLow + 1; | ||
| 150 | + for (u32 k = 0; k + 1 < algoCount; k++) { | ||
| 151 | + pIndices[algoLow + k] = physLow + k; | ||
| 152 | + } | ||
| 153 | + pIndices[algoHigh] = physHigh; | ||
| 154 | +} | ||
| 155 | + | ||
| 156 | +// hostdpu 强约束:最高算法层锚定 HOST 且 localRanks==userRankSize 的物理层(从高到低找首个), | ||
| 157 | +// 并校验 HOST 锚点以下物理层数 >= 剩余待匹配算法层数;找不到或不满足则 not support | ||
| 158 | +static HcclResult AnchorHostDpu(const std::vector<PhysicalLevelInfo>& physicalLevels, | ||
| 159 | + const std::vector<u32>& effIdx, u32 userRankSize, u32 topAlgo, u32& topPhysPos, | ||
| 160 | + std::set<u32>& anchoredPhys, std::map<u32, u32>& anchors) | ||
| 161 | +{ | ||
| 162 | + bool found = false; | ||
| 163 | + for (int32_t k = static_cast<int32_t>(effIdx.size()) - 1; k >= 0; k--) { | ||
| 164 | + const PhysicalLevelInfo& lvl = physicalLevels[effIdx[k]]; | ||
| 165 | + if (lvl.locType == EndpointLocType::ENDPOINT_LOC_TYPE_HOST && lvl.localRanks.size() == userRankSize) { | ||
| 166 | + anchors[topAlgo] = static_cast<u32>(k); | ||
| 167 | + anchoredPhys.insert(static_cast<u32>(k)); | ||
| 168 | + topPhysPos = static_cast<u32>(k); | ||
| 169 | + found = true; | ||
| 170 | + break; | ||
| 171 | + } | ||
| 172 | + } | ||
| 173 | + if (!found) { | ||
| 174 | + HCCL_INFO("[FindAnchors] hostdpu but no HOST layer with localRanks==userRankSize[%u], not support.", | ||
| 175 | + userRankSize); | ||
| 176 | + return HcclResult::HCCL_E_NOT_SUPPORT; | ||
| 177 | + } | ||
| 178 | + // HOST 锚点以下的物理层数(= topPhysPos)须 >= 剩余待匹配的算法层数(= topAlgo),否则低层无足够物理层 | ||
| 179 | + if (topPhysPos < topAlgo) { | ||
| 180 | + HCCL_INFO("[FindAnchors] hostdpu phys layers below host[%u] < remaining algo levels[%u], not support.", | ||
| 181 | + topPhysPos, topAlgo); | ||
| 182 | + return HcclResult::HCCL_E_NOT_SUPPORT; | ||
| 183 | + } | ||
| 184 | + return HcclResult::HCCL_SUCCESS; | ||
| 185 | +} | ||
| 186 | + | ||
| 187 | +// Mesh 锚点:算法层从低到高遍历,优先匹配 COMM_TOPO_1DMESH 物理层,不可重复锚定; | ||
| 188 | +// hostdpu 已锚定的最高层跳过;MeshConcur 未匹配到则 not support | ||
| 189 | +static HcclResult AnchorMeshLevels(const std::vector<PhysicalLevelInfo>& physicalLevels, | ||
| 190 | + const std::vector<u32>& effIdx, const std::vector<AlgoType>& algoTypes, u32 topAlgo, u32 topPhysPos, | ||
| 191 | + std::set<u32>& anchoredPhys, std::map<u32, u32>& anchors) | ||
| 192 | +{ | ||
| 193 | + for (u32 i = 0; i < algoTypes.size(); i++) { | ||
| 194 | + if (anchors.count(i) > 0) { | ||
| 195 | + continue; | ||
| 196 | + } | ||
| 197 | + if (!IsMeshAlgo(algoTypes[i])) { | ||
| 198 | + continue; | ||
| 199 | + } | ||
| 200 | + // 为上层算法层(i+1..topAlgo)留足物理位:candidateHigh = topPhysPos - (topAlgo - i) | ||
| 201 | + u32 candidateHigh = topPhysPos - (topAlgo - i); | ||
| 202 | + bool found = false; | ||
| 203 | + for (u32 k = i; k <= candidateHigh; k++) { | ||
| 204 | + if (anchoredPhys.count(k) > 0) { | ||
| 205 | + continue; | ||
| 206 | + } | ||
| 207 | + if (physicalLevels[effIdx[k]].topoType == COMM_TOPO_1DMESH) { | ||
| 208 | + anchors[i] = k; | ||
| 209 | + anchoredPhys.insert(k); | ||
| 210 | + found = true; | ||
| 211 | + break; | ||
| 212 | + } | ||
| 213 | + } | ||
| 214 | + if (!found && IsMeshConcurAlgo(algoTypes[i])) { | ||
| 215 | + HCCL_INFO("[FindAnchors] algo[%u] MeshConcur but no Mesh layer, not support.", i); | ||
| 216 | + return HcclResult::HCCL_E_NOT_SUPPORT; | ||
| 217 | + } | ||
| 218 | + } | ||
| 219 | + return HcclResult::HCCL_SUCCESS; | ||
| 220 | +} | ||
| 221 | + | ||
| 222 | +// 锚点匹配:hostdpu 强约束最高层选 HOST(优先级高于 Mesh)+ Mesh 层优先匹配 COMM_TOPO_1DMESH | ||
| 223 | +HcclResult FindAnchors(const std::vector<PhysicalLevelInfo>& physicalLevels, const std::vector<u32>& effIdx, | ||
| 224 | + const std::vector<AlgoType>& algoTypes, OpExecuteConfig engine, u32 userRankSize, | ||
| 225 | + std::map<u32, u32>& anchors) | ||
| 226 | +{ | ||
| 227 | + std::set<u32> anchoredPhys; | ||
| 228 | + u32 topAlgo = static_cast<u32>(algoTypes.size()) - 1; | ||
| 229 | + // 最高算法层对应的物理 effIdx 位置:非 hostdpu 由尾段取 effIdx.back();hostdpu 取 HOST 锚点 | ||
| 230 | + u32 topPhysPos = static_cast<u32>(effIdx.size()) - 1; | ||
| 231 | + if (engine == OpExecuteConfig::HOSTCPU) { | ||
| 232 | + CHK_RET(AnchorHostDpu(physicalLevels, effIdx, userRankSize, topAlgo, topPhysPos, anchoredPhys, anchors)); | ||
| 233 | + } | ||
| 234 | + CHK_RET(AnchorMeshLevels(physicalLevels, effIdx, algoTypes, topAlgo, topPhysPos, anchoredPhys, anchors)); | ||
| 235 | + return HcclResult::HCCL_SUCCESS; | ||
| 236 | +} | ||
| 237 | + | ||
| 238 | +// 分段压缩:按锚点将算法层与物理层分段,每段低层一一 + 最高层压缩多余物理层 | ||
| 239 | +HcclResult ResolveSegmentMapping(const std::vector<u32>& effIdx, const std::vector<AlgoType>& algoTypes, | ||
| 240 | + const std::map<u32, u32>& anchors, std::vector<u32>& pIndices) | ||
| 241 | +{ | ||
| 242 | + pIndices.resize(algoTypes.size(), INVALID_UINT); | ||
| 243 | + u32 algoStart = 0; | ||
| 244 | + u32 physStart = 0; | ||
| 245 | + for (const auto& [anchorAlgo, anchorPhys] : anchors) { | ||
| 246 | + // 前段存在当且仅当锚点之前同时有算法层与物理层;anchorAlgo==algoStart 时 anchorAlgo-1 会 u32 下溢,须跳过 | ||
| 247 | + if (anchorAlgo > algoStart && anchorPhys > physStart) { | ||
| 248 | + MatchLayerIdxBySegment(algoStart, anchorAlgo - 1, physStart, anchorPhys - 1, pIndices); | ||
| 249 | + } | ||
| 250 | + pIndices[anchorAlgo] = anchorPhys; | ||
| 251 | + algoStart = anchorAlgo + 1; | ||
| 252 | + physStart = anchorPhys + 1; | ||
| 253 | + } | ||
| 254 | + MatchLayerIdxBySegment(algoStart, algoTypes.size() - 1, physStart, effIdx.size() - 1, pIndices); | ||
| 255 | + return HcclResult::HCCL_SUCCESS; | ||
| 256 | +} | ||
| 257 | + | ||
| 258 | +// 在 meshEffPos 之上(更高 index)找首个 localRanks 包含 mesh 层 localRanks 的物理层;找不到返回 INVALID_PHYSICAL_LEVEL_IDX | ||
| 259 | +int32_t FindUpperEncompassingLevel(const std::vector<PhysicalLevelInfo>& physicalLevels, | ||
| 260 | + const std::vector<u32>& effIdx, u32 meshEffPos) | ||
| 261 | +{ | ||
| 262 | + const auto& meshRanks = physicalLevels[effIdx[meshEffPos]].localRanks; | ||
| 263 | + for (u32 k = meshEffPos + 1; k < effIdx.size(); k++) { | ||
| 264 | + const auto& upperRanks = physicalLevels[effIdx[k]].localRanks; | ||
| 265 | + if (std::includes(upperRanks.begin(), upperRanks.end(), meshRanks.begin(), meshRanks.end())) { | ||
| 266 | + return static_cast<int32_t>(k); | ||
| 267 | + } | ||
| 268 | + } | ||
| 269 | + return INVALID_PHYSICAL_LEVEL_IDX; | ||
| 270 | +} | ||
| 271 | + | ||
| 272 | +// 引擎过滤 + 锚点匹配 + 分段,得 effIdx 与 pIndices;校验最高层 localRanks==userRankSize | ||
| 273 | +HcclResult ResolveMapping(const std::vector<PhysicalLevelInfo>& physicalLevels, const AlgorithmProfile& profile, | ||
| 274 | + u32 userRankSize, std::vector<u32>& effIdx, std::vector<u32>& pIndices) | ||
| 275 | +{ | ||
| 276 | + effIdx = CollectEffectiveIndices(physicalLevels, profile.engine); | ||
| 277 | + u32 algoLevelNum = profile.algoTypes.size(); | ||
| 278 | + if (effIdx.size() < algoLevelNum) { | ||
| 279 | + HCCL_INFO("[ResolveMapping] valid level num[%zu] < algoLevelNum[%u].", effIdx.size(), algoLevelNum); | ||
| 280 | + return HcclResult::HCCL_E_NOT_SUPPORT; | ||
| 281 | + } | ||
| 282 | + std::map<u32, u32> anchors; | ||
| 283 | + // 锚点匹配:含 MeshConcur 的 1DMESH 校验与 hostdpu 强约束,须无条件执行(1:1 时也需校验底层 1DMESH) | ||
| 284 | + CHK_RET(FindAnchors(physicalLevels, effIdx, profile.algoTypes, profile.engine, userRankSize, anchors)); | ||
| 285 | + CHK_RET(ResolveSegmentMapping(effIdx, profile.algoTypes, anchors, pIndices)); | ||
| 286 | + // 最高算法层 localRanks 必须等于 userRankSize | ||
| 287 | + u32 topPhys = effIdx[pIndices[algoLevelNum - 1]]; | ||
| 288 | + if (physicalLevels[topPhys].localRanks.size() != userRankSize) { | ||
| 289 | + HCCL_INFO("[ResolveMapping] top layer localRanks[%zu] != userRankSize[%u].", | ||
| 290 | + physicalLevels[topPhys].localRanks.size(), userRankSize); | ||
| 291 | + return HcclResult::HCCL_E_NOT_SUPPORT; | ||
| 292 | + } | ||
| 293 | + return HcclResult::HCCL_SUCCESS; | ||
| 294 | +} | ||
| 295 | + | ||
| 296 | +// 填充 physicalIdxForAlgoLevels(二级):MeshConcur 层记 {Mesh层, 上层超集层},普通层记 {该层} | ||
| 297 | +HcclResult FillPhysicalIdxForAlgoLevels(const std::vector<PhysicalLevelInfo>& physicalLevels, | ||
| 298 | + const std::vector<u32>& effIdx, const std::vector<u32>& pIndices, const std::vector<AlgoType>& algoTypes, | ||
| 299 | + std::vector<std::vector<PhysicalLevelIndex>>& physicalIdxForAlgoLevels) | ||
| 300 | +{ | ||
| 301 | + physicalIdxForAlgoLevels.resize(algoTypes.size()); | ||
| 302 | + for (u32 i = 0; i < algoTypes.size(); i++) { | ||
| 303 | + u32 physIdx = effIdx[pIndices[i]]; | ||
| 304 | + if (IsMeshConcurAlgo(algoTypes[i])) { | ||
| 305 | + int32_t upperPos = FindUpperEncompassingLevel(physicalLevels, effIdx, pIndices[i]); | ||
| 306 | + if (upperPos == INVALID_PHYSICAL_LEVEL_IDX) { | ||
| 307 | + HCCL_INFO("[FillPhysicalIdx] level[%u] MeshConcur no upper encompassing layer, not support.", i); | ||
| 308 | + return HcclResult::HCCL_E_NOT_SUPPORT; | ||
| 309 | + } | ||
| 310 | + physicalIdxForAlgoLevels[i] = {static_cast<PhysicalLevelIndex>(physIdx), | ||
| 311 | + static_cast<PhysicalLevelIndex>(effIdx[upperPos])}; | ||
| 312 | + } else { | ||
| 313 | + physicalIdxForAlgoLevels[i] = {static_cast<PhysicalLevelIndex>(physIdx)}; | ||
| 314 | + } | ||
| 315 | + } | ||
| 316 | + return HcclResult::HCCL_SUCCESS; | ||
| 317 | +} | ||
| 318 | + | ||
| 319 | +// 根据算法名构造 AlgorithmProfile:algoTypes 从算法名中的 Mesh/NHR/Concur 等关键字推断 | ||
| 320 | +AlgorithmProfile BuildAlgorithmProfile(const std::string& algName) | ||
| 321 | +{ | ||
| 322 | + AlgorithmProfile profile; | ||
| 323 | + profile.name = algName; | ||
| 324 | + profile.engine = OpExecuteConfig::AICPU_TS; | ||
| 325 | + profile.opType = HcclCMDType::HCCL_CMD_ALLREDUCE; | ||
| 326 | + | ||
| 327 | + if (algName == "AicpuAllReduceSoleMeshOneShot") { | ||
| 328 | + profile.algoTypes = {AlgoType::MESH_ONESHOT}; | ||
| 329 | + } else if (algName == "AicpuAllReduceSoleMeshTwoShot") { | ||
| 330 | + profile.algoTypes = {AlgoType::MESH_TWOSHOT}; | ||
| 331 | + } else if (algName == "AicpuAllReduceSoleMeshChunkTwoShot") { | ||
| 332 | + profile.algoTypes = {AlgoType::MESH_CHUNK_TWOSHOT}; | ||
| 333 | + } else if (algName == "AicpuAllReduceSoleMeshConcur") { | ||
| 334 | + profile.algoTypes = {AlgoType::MESH_CONCUR}; | ||
| 335 | + } else if (algName == "AicpuAllReduceParallelMeshNHR") { | ||
| 336 | + profile.algoTypes = {AlgoType::MESH, AlgoType::NHR}; | ||
| 337 | + } else if (algName == "AicpuAllReduceSequenceMeshConcurNHR") { | ||
| 338 | + profile.algoTypes = {AlgoType::MESH_CONCUR, AlgoType::NHR}; | ||
| 339 | + } else if (algName == "AicpuAllReduceSequenceMeshConcurNHRNHR") { | ||
| 340 | + profile.algoTypes = {AlgoType::MESH_CONCUR, AlgoType::NHR, AlgoType::NHR}; | ||
| 341 | + } else if (algName == "DpuAllReduceSequenceMeshNHR") { | ||
| 342 | + profile.engine = OpExecuteConfig::HOSTCPU; | ||
| 343 | + profile.algoTypes = {AlgoType::MESH, AlgoType::NHR}; | ||
| 344 | + } else if (algName == "InsAllReduceParallelRSAGUBX") { | ||
| 345 | + profile.algoTypes = {AlgoType::MESH, AlgoType::NHR}; | ||
| 346 | + } else if (algName == "CcuSchedAllReduceParallelMeshNHR") { | ||
| 347 | + profile.algoTypes = {AlgoType::MESH, AlgoType::NHR}; | ||
| 348 | + } else { | ||
| 349 | + HCCL_WARNING("[BuildAlgorithmProfile] unknown algName[%s], algoTypes left empty.", algName.c_str()); | ||
| 350 | + } | ||
| 351 | + return profile; | ||
| 352 | +} | ||
| 353 | + | ||
| 354 | +} // namespace ops_hccl | ||
| @@ -0,0 +1,111 @@ | |||
| 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 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | +namespace ops_hccl { | ||
| 22 | + | ||
| 23 | +// topo match 相关常量 | ||
| 24 | +constexpr int32_t INVALID_PHYSICAL_LEVEL_IDX = -1; | ||
| 25 | +constexpr u32 ALGO_LEVEL_NUM_TWO = 2; | ||
| 26 | +constexpr u32 ALGO_LEVEL_NUM_THREE = 3; | ||
| 27 | +constexpr u32 CONCURRENT_SUBGROUP_NUM = 2; | ||
| 28 | + | ||
| 29 | +// Mesh 类算法集合与 NHR 类算法集合 | ||
| 30 | +const std::set<AlgoType> MESH_ALGO_TYPES = { | ||
| 31 | + AlgoType::MESH, AlgoType::MESH_2DIE, AlgoType::MESH_ONESHOT, AlgoType::MESH_TWOSHOT, | ||
| 32 | + AlgoType::MESH_CONCUR, AlgoType::MESH_MULTILINK, AlgoType::MESH_CHUNK, AlgoType::MESH_CHUNK_TWOSHOT, | ||
| 33 | + AlgoType::MESH_SINGLE_CHANNEL, AlgoType::MESH_CONCURRENT | ||
| 34 | +}; | ||
| 35 | +// NHR 类算法集合 | ||
| 36 | +const std::set<AlgoType> NHR_ALGO_TYPES = { | ||
| 37 | + AlgoType::NHR, AlgoType::NHR_MULTILINK, AlgoType::NHR_AICPU_REDUCE | ||
| 38 | +}; | ||
| 39 | +// MeshConcur 类算法(MESH_CONCUR 与 MESH_CONCURRENT 都触发 CLOS 双层规则) | ||
| 40 | +const std::set<AlgoType> MESH_CONCUR_ALGO_TYPES = { | ||
| 41 | + AlgoType::MESH_CONCUR, AlgoType::MESH_CONCURRENT | ||
| 42 | +}; | ||
| 43 | + | ||
| 44 | +// 辗转相除求两数最大公约数;a 或 b 为 0 时返回 1 避免退化 | ||
| 45 | +u32 CalcGcdByPair(u32 a, u32 b); | ||
| 46 | + | ||
| 47 | +// 对一组数逐对归约求最大公约数,result==1 时早停 | ||
| 48 | +u32 CalcGcd(const std::vector<u32>& nums); | ||
| 49 | + | ||
| 50 | +// 从高到低找首个 hasTopoInst 的物理层序号;不存在返回 INVALID_PHYSICAL_LEVEL_IDX | ||
| 51 | +int32_t FindHighestEffectiveLevel(const std::vector<PhysicalLevelInfo>& physicalLevels); | ||
| 52 | + | ||
| 53 | +// instList 各元素是否全等(对称判定) | ||
| 54 | +bool IsInstListSymmetric(const std::vector<uint32_t>& instList); | ||
| 55 | + | ||
| 56 | +// 构造跨层代表 rank:count 个,从 offset 起、按 step 步长(offset 取 myRank 在本层的偏移,保证 myRank 命中) | ||
| 57 | +std::vector<u32> BuildRepresentativeGroup(u32 step, u32 count, u32 offset); | ||
| 58 | + | ||
| 59 | +// 校验单个 group:规模等于 dim 且包含 myRank;失败打 ERROR 并返回 HCCL_E_INTERNAL | ||
| 60 | +HcclResult ValidateGroup(const std::vector<u32>& group, u32 dim, u32 myRank, const std::string& levelName); | ||
| 61 | + | ||
| 62 | +// 引擎过滤:非 hostdpu 排除 HOST 层,AIV 排除含 UBG 链路的层 | ||
| 63 | +std::vector<u32> CollectEffectiveIndices(const std::vector<PhysicalLevelInfo>& physicalLevels, | ||
| 64 | + OpExecuteConfig engine); | ||
| 65 | + | ||
| 66 | +// 判断算法是否属于 Mesh 类 | ||
| 67 | +bool IsMeshAlgo(AlgoType algo); | ||
| 68 | + | ||
| 69 | +// 判断算法是否属于 MeshConcur 类(触发 CLOS 双层规则) | ||
| 70 | +bool IsMeshConcurAlgo(AlgoType algo); | ||
| 71 | + | ||
| 72 | +// 锚点匹配:hostdpu 强约束最高算法层锚定 HOST 且 localRanks==userRankSize 的物理层;Mesh 算法优先匹配 COMM_TOPO_1DMESH 物理层(不可重复锚定) | ||
| 73 | +HcclResult FindAnchors(const std::vector<PhysicalLevelInfo>& physicalLevels, const std::vector<u32>& effIdx, | ||
| 74 | + const std::vector<AlgoType>& algoTypes, OpExecuteConfig engine, u32 userRankSize, | ||
| 75 | + std::map<u32, u32>& anchors); | ||
| 76 | + | ||
| 77 | +// 分段压缩得各算法层对应的物理层 effIdx position | ||
| 78 | +HcclResult ResolveSegmentMapping(const std::vector<u32>& effIdx, const std::vector<AlgoType>& algoTypes, | ||
| 79 | + const std::map<u32, u32>& anchors, std::vector<u32>& pIndices); | ||
| 80 | + | ||
| 81 | +// 引擎过滤 + 锚点匹配 + 分段,得 effIdx 与 pIndices;校验最高层 localRanks==userRankSize | ||
| 82 | +HcclResult ResolveMapping(const std::vector<PhysicalLevelInfo>& physicalLevels, const AlgorithmProfile& profile, | ||
| 83 | + u32 userRankSize, std::vector<u32>& effIdx, std::vector<u32>& pIndices); | ||
| 84 | + | ||
| 85 | +// 在 meshEffPos 之上找首个 localRanks 包含 mesh 层 localRanks 的物理层 | ||
| 86 | +int32_t FindUpperEncompassingLevel(const std::vector<PhysicalLevelInfo>& physicalLevels, | ||
| 87 | + const std::vector<u32>& effIdx, u32 meshEffPos); | ||
| 88 | + | ||
| 89 | +// 填充 physicalIdxForAlgoLevels(二级):MeshConcur 层记 {Mesh层, 上层超集层},普通层记 {该层} | ||
| 90 | +HcclResult FillPhysicalIdxForAlgoLevels(const std::vector<PhysicalLevelInfo>& physicalLevels, | ||
| 91 | + const std::vector<u32>& effIdx, const std::vector<u32>& pIndices, const std::vector<AlgoType>& algoTypes, | ||
| 92 | + std::vector<std::vector<PhysicalLevelIndex>>& physicalIdxForAlgoLevels); | ||
| 93 | + | ||
| 94 | +// V2 基类:MatchTopo 增加 AlgorithmProfile 参数 | ||
| 95 | +class TopoMatchBaseV2 { | ||
| 96 | +public: | ||
| 97 | + explicit TopoMatchBaseV2(); | ||
| 98 | + virtual ~TopoMatchBaseV2(); | ||
| 99 | + | ||
| 100 | + virtual std::string Describe() const = 0; | ||
| 101 | + | ||
| 102 | + virtual HcclResult MatchTopo(HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo, | ||
| 103 | + AlgHierarchyInfoForAllLevel& algHierarchyInfo, const AlgorithmProfile& profile) = 0; | ||
| 104 | +}; | ||
| 105 | + | ||
| 106 | +// 根据算法名构造 AlgorithmProfile(algoTypes 从算法名推断) | ||
| 107 | +AlgorithmProfile BuildAlgorithmProfile(const std::string& algName); | ||
| 108 | + | ||
| 109 | +} // namespace ops_hccl | ||
| 110 | + | ||
| 111 | + | ||
| @@ -0,0 +1,61 @@ | |||
| 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 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | +namespace ops_hccl { | ||
| 16 | + | ||
| 17 | +TopoMatchConcurrentV2::TopoMatchConcurrentV2() {} | ||
| 18 | + | ||
| 19 | +TopoMatchConcurrentV2::~TopoMatchConcurrentV2() {} | ||
| 20 | + | ||
| 21 | +HcclResult TopoMatchConcurrentV2::MatchTopo(HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo, | ||
| 22 | + AlgHierarchyInfoForAllLevel& algHierarchyInfo, const AlgorithmProfile& profile) | ||
| 23 | +{ | ||
| 24 | + (void)comm; | ||
| 25 | + u32 myRank = topoInfo->userRank; | ||
| 26 | + const auto& physicalLevels = topoInfo->physicalLevels; | ||
| 27 | + if (physicalLevels.empty()) { | ||
| 28 | + HCCL_ERROR("[TopoMatchConcurrentV2] Rank [%u], physicalLevels is empty.", myRank); | ||
| 29 | + return HcclResult::HCCL_E_INTERNAL; | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + // 引擎过滤后收集有效层 | ||
| 33 | + std::vector<u32> effIdx = CollectEffectiveIndices(physicalLevels, profile.engine); | ||
| 34 | + u32 effNum = effIdx.size(); | ||
| 35 | + CHK_PRT_RET(effNum == 0 || effNum > ALGO_LEVEL_NUM_TWO, | ||
| 36 | + HCCL_INFO("[TopoMatchConcurrentV2] Rank [%u], level num[%u] not support.", myRank, effNum), | ||
| 37 | + HcclResult::HCCL_E_NOT_SUPPORT); | ||
| 38 | + CHK_PRT_RET((topoInfo->userRankSize == 0), | ||
| 39 | + HCCL_ERROR("[TopoMatchConcurrentV2] Rank [%d], rankSize is 0.", myRank), | ||
| 40 | + HcclResult::HCCL_E_INTERNAL); | ||
| 41 | + | ||
| 42 | + // infos 沿用原 Concurrent:两组同 rank(mesh 组 + clos 组并发),不依赖 physicalLevels 内容 | ||
| 43 | + std::vector<u32> rankIds; | ||
| 44 | + rankIds.reserve(topoInfo->userRankSize); | ||
| 45 | + for (u32 rankId = 0; rankId < topoInfo->userRankSize; rankId++) { | ||
| 46 | + rankIds.push_back(rankId); | ||
| 47 | + } | ||
| 48 | + algHierarchyInfo.infos.resize(1); | ||
| 49 | + algHierarchyInfo.infos[0].resize(CONCURRENT_SUBGROUP_NUM); | ||
| 50 | + algHierarchyInfo.infos[0][0] = rankIds; | ||
| 51 | + algHierarchyInfo.infos[0][1] = rankIds; | ||
| 52 | + | ||
| 53 | + // physicalIdx 指向最高有效层 | ||
| 54 | + u32 highestIdx = effIdx.back(); | ||
| 55 | + algHierarchyInfo.physicalIdxForAlgoLevels = {{static_cast<PhysicalLevelIndex>(highestIdx)}}; | ||
| 56 | + HCCL_INFO("[TopoMatchConcurrentV2] Rank [%u], rankSize[%u], physIdx[%u].", myRank, topoInfo->userRankSize, | ||
| 57 | + highestIdx); | ||
| 58 | + return HcclResult::HCCL_SUCCESS; | ||
| 59 | +} | ||
| 60 | + | ||
| 61 | +} // namespace ops_hccl | ||
| @@ -0,0 +1,34 @@ | |||
| 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 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | +namespace ops_hccl { | ||
| 17 | + | ||
| 18 | +class TopoMatchConcurrentV2 : public TopoMatchBaseV2 { | ||
| 19 | +public: | ||
| 20 | + explicit TopoMatchConcurrentV2(); | ||
| 21 | + ~TopoMatchConcurrentV2() override; | ||
| 22 | + | ||
| 23 | + std::string Describe() const override | ||
| 24 | + { | ||
| 25 | + return "Topo Match for Concurrent Algorithm V2 (supports 950/960 out-place devices)."; | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | + HcclResult MatchTopo(HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo, | ||
| 29 | + AlgHierarchyInfoForAllLevel& algHierarchyInfo, const AlgorithmProfile& profile) override; | ||
| 30 | +}; | ||
| 31 | + | ||
| 32 | +} // namespace ops_hccl | ||
| 33 | + | ||
| 34 | + | ||
| @@ -0,0 +1,76 @@ | |||
| 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 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | +namespace ops_hccl { | ||
| 16 | + | ||
| 17 | +TopoMatchOneLevel::TopoMatchOneLevel() {} | ||
| 18 | + | ||
| 19 | +TopoMatchOneLevel::~TopoMatchOneLevel() {} | ||
| 20 | + | ||
| 21 | +HcclResult TopoMatchOneLevel::MatchTopo(HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo, | ||
| 22 | + AlgHierarchyInfoForAllLevel& algHierarchyInfo, const AlgorithmProfile& profile) | ||
| 23 | +{ | ||
| 24 | + (void)comm; | ||
| 25 | + const auto& physicalLevels = topoInfo->physicalLevels; | ||
| 26 | + if (physicalLevels.empty() || topoInfo->userRankSize == 0) { | ||
| 27 | + HCCL_ERROR("[TopoMatchOneLevel] Rank [%u], physicalLevels empty or userRankSize 0.", topoInfo->userRank); | ||
| 28 | + return HcclResult::HCCL_E_INTERNAL; | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + // 引擎过滤后收集有效层 | ||
| 32 | + std::vector<u32> effIdx = CollectEffectiveIndices(physicalLevels, profile.engine); | ||
| 33 | + if (effIdx.empty()) { | ||
| 34 | + HCCL_INFO("[TopoMatchOneLevel] Rank [%u], no valid layer after engine filter.", topoInfo->userRank); | ||
| 35 | + return HcclResult::HCCL_E_NOT_SUPPORT; | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + // hostdpu:找 localRanks==userRankSize 且 locType==HOST 的层 | ||
| 39 | + u32 picked = INVALID_UINT; | ||
| 40 | + if (profile.engine == OpExecuteConfig::HOSTCPU) { | ||
| 41 | + for (u32 idx : effIdx) { | ||
| 42 | + if (physicalLevels[idx].localRanks.size() == topoInfo->userRankSize && | ||
| 43 | + physicalLevels[idx].locType == EndpointLocType::ENDPOINT_LOC_TYPE_HOST) { | ||
| 44 | + picked = idx; | ||
| 45 | + break; | ||
| 46 | + } | ||
| 47 | + } | ||
| 48 | + if (picked == INVALID_UINT) { | ||
| 49 | + HCCL_INFO("[TopoMatchOneLevel] Rank [%u], hostdpu but no HOST layer with full localRanks.", | ||
| 50 | + topoInfo->userRank); | ||
| 51 | + return HcclResult::HCCL_E_NOT_SUPPORT; | ||
| 52 | + } | ||
| 53 | + } else { | ||
| 54 | + // 非 hostdpu:取 localRanks==userRankSize 的最低有效层 | ||
| 55 | + for (u32 idx : effIdx) { | ||
| 56 | + if (physicalLevels[idx].localRanks.size() == topoInfo->userRankSize) { | ||
| 57 | + picked = idx; | ||
| 58 | + break; | ||
| 59 | + } | ||
| 60 | + } | ||
| 61 | + if (picked == INVALID_UINT) { | ||
| 62 | + HCCL_INFO("[TopoMatchOneLevel] Rank [%u], no layer with localRanks == userRankSize.", topoInfo->userRank); | ||
| 63 | + return HcclResult::HCCL_E_NOT_SUPPORT; | ||
| 64 | + } | ||
| 65 | + } | ||
| 66 | + | ||
| 67 | + algHierarchyInfo.infos.resize(1); | ||
| 68 | + algHierarchyInfo.infos[0].resize(1); | ||
| 69 | + algHierarchyInfo.infos[0][0] = physicalLevels[picked].localRanks; | ||
| 70 | + algHierarchyInfo.physicalIdxForAlgoLevels = {{static_cast<PhysicalLevelIndex>(picked)}}; | ||
| 71 | + HCCL_INFO("[TopoMatchOneLevel] Rank [%u], pick physicalLevel[%u], size[%zu].", | ||
| 72 | + topoInfo->userRank, picked, physicalLevels[picked].localRanks.size()); | ||
| 73 | + return HcclResult::HCCL_SUCCESS; | ||
| 74 | +} | ||
| 75 | + | ||
| 76 | +} // namespace ops_hccl | ||
| @@ -0,0 +1,34 @@ | |||
| 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 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | +namespace ops_hccl { | ||
| 17 | + | ||
| 18 | +class TopoMatchOneLevel : public TopoMatchBaseV2 { | ||
| 19 | +public: | ||
| 20 | + explicit TopoMatchOneLevel(); | ||
| 21 | + ~TopoMatchOneLevel() override; | ||
| 22 | + | ||
| 23 | + std::string Describe() const override | ||
| 24 | + { | ||
| 25 | + return "Topo Match for One Level Algorithm."; | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | + HcclResult MatchTopo(HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo, | ||
| 29 | + AlgHierarchyInfoForAllLevel& algHierarchyInfo, const AlgorithmProfile& profile) override; | ||
| 30 | +}; | ||
| 31 | + | ||
| 32 | +} // namespace ops_hccl | ||
| 33 | + | ||
| 34 | + | ||
| @@ -0,0 +1,118 @@ | |||
| 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 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | +namespace ops_hccl { | ||
| 16 | + | ||
| 17 | +namespace { | ||
| 18 | +// 校验 level 对称并取维度:GLOBAL 看 instList 是否全等;LOCAL 视为对称 | ||
| 19 | +HcclResult ValidateLevelAndCalcDim(u32 levelIdx, const std::vector<PhysicalLevelInfo>& physicalLevels, | ||
| 20 | + bool& symmetricOut, u32& dim) | ||
| 21 | +{ | ||
| 22 | + const PhysicalLevelInfo& level = physicalLevels[levelIdx]; | ||
| 23 | + if (level.view == PhysicalLevelView::LOCAL) { | ||
| 24 | + // LOCAL 无全局 instList,对称性由其上级 netLayer 层判定 | ||
| 25 | + dim = static_cast<u32>(level.localRanks.size()); | ||
| 26 | + symmetricOut = true; | ||
| 27 | + return HcclResult::HCCL_SUCCESS; | ||
| 28 | + } | ||
| 29 | + if (!IsInstListSymmetric(level.instSizeListByLayer)) { | ||
| 30 | + symmetricOut = false; | ||
| 31 | + return HcclResult::HCCL_SUCCESS; | ||
| 32 | + } | ||
| 33 | + symmetricOut = true; | ||
| 34 | + dim = static_cast<u32>(level.localRanks.size()); | ||
| 35 | + return HcclResult::HCCL_SUCCESS; | ||
| 36 | +} | ||
| 37 | + | ||
| 38 | +// ThreeLevel 不支持非对称:p_0/p_1 任一非对称即 not support;维度 d0/d1/d2 | ||
| 39 | +HcclResult CalcDimsAndCheckSymmetry(const std::vector<PhysicalLevelInfo>& physicalLevels, | ||
| 40 | + u32 phys0, u32 phys1, u32 userRankSize, u32 myRank, u32& d0, u32& d1, u32& d2) | ||
| 41 | +{ | ||
| 42 | + u32 level1TotalSize = 0; | ||
| 43 | + bool sym0 = false; | ||
| 44 | + bool sym1 = false; | ||
| 45 | + CHK_RET(ValidateLevelAndCalcDim(phys0, physicalLevels, sym0, d0)); | ||
| 46 | + CHK_RET(ValidateLevelAndCalcDim(phys1, physicalLevels, sym1, level1TotalSize)); | ||
| 47 | + if (!sym0 || !sym1) { | ||
| 48 | + HCCL_INFO("[TopoMatchThreeLevel] Rank [%u], asymmetric detected, not support.", myRank); | ||
| 49 | + return HcclResult::HCCL_E_NOT_SUPPORT; | ||
| 50 | + } | ||
| 51 | + if (d0 == 0 || level1TotalSize == 0 || level1TotalSize % d0 != 0) { | ||
| 52 | + HCCL_INFO("[TopoMatchThreeLevel] Rank [%u], level1TotalSize[%u] not divisible by d0[%u].", myRank, | ||
| 53 | + level1TotalSize, d0); | ||
| 54 | + return HcclResult::HCCL_E_NOT_SUPPORT; | ||
| 55 | + } | ||
| 56 | + d1 = level1TotalSize / d0; | ||
| 57 | + if (userRankSize % d0 != 0 || (userRankSize / d0) % d1 != 0) { | ||
| 58 | + HCCL_INFO("[TopoMatchThreeLevel] Rank [%u], userRankSize[%u] not divisible by d0[%u]*d1[%u].", myRank, | ||
| 59 | + userRankSize, d0, d1); | ||
| 60 | + return HcclResult::HCCL_E_NOT_SUPPORT; | ||
| 61 | + } | ||
| 62 | + d2 = userRankSize / d0 / d1; | ||
| 63 | + return HcclResult::HCCL_SUCCESS; | ||
| 64 | +} | ||
| 65 | +} // namespace | ||
| 66 | + | ||
| 67 | +TopoMatchThreeLevel::TopoMatchThreeLevel() {} | ||
| 68 | +TopoMatchThreeLevel::~TopoMatchThreeLevel() {} | ||
| 69 | + | ||
| 70 | +HcclResult TopoMatchThreeLevel::MatchTopo(HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo, | ||
| 71 | + AlgHierarchyInfoForAllLevel& algHierarchyInfo, const AlgorithmProfile& profile) | ||
| 72 | +{ | ||
| 73 | + (void)comm; | ||
| 74 | + const auto& physicalLevels = topoInfo->physicalLevels; | ||
| 75 | + u32 myRank = topoInfo->userRank; | ||
| 76 | + u32 userRankSize = topoInfo->userRankSize; | ||
| 77 | + if (physicalLevels.empty() || userRankSize == 0 || profile.algoTypes.size() != ALGO_LEVEL_NUM_THREE) { | ||
| 78 | + HCCL_ERROR("[TopoMatchThreeLevel] Rank [%u], invalid input.", myRank); | ||
| 79 | + return HcclResult::HCCL_E_INTERNAL; | ||
| 80 | + } | ||
| 81 | + | ||
| 82 | + // 引擎过滤 + 锚点匹配 + 分段 + 最高层校验 | ||
| 83 | + std::vector<u32> effIdx; | ||
| 84 | + std::vector<u32> pIndices; | ||
| 85 | + CHK_RET(ResolveMapping(physicalLevels, profile, userRankSize, effIdx, pIndices)); | ||
| 86 | + u32 phys0 = effIdx[pIndices[0]]; | ||
| 87 | + u32 phys1 = effIdx[pIndices[1]]; | ||
| 88 | + | ||
| 89 | + // 非对称判定 + 维度计算(ThreeLevel 不支持非对称) | ||
| 90 | + u32 d0 = 0; | ||
| 91 | + u32 d1 = 0; | ||
| 92 | + u32 d2 = 0; | ||
| 93 | + CHK_RET(CalcDimsAndCheckSymmetry(physicalLevels, phys0, phys1, userRankSize, myRank, d0, d1, d2)); | ||
| 94 | + | ||
| 95 | + // 构造 infos;level1 代表环须落在 myRank 所在 level1 instance 内,故 offset 取 instance 基址 + 层内偏移 | ||
| 96 | + std::vector<u32> group0 = physicalLevels[phys0].localRanks; | ||
| 97 | + u32 level1Base = (myRank / (d0 * d1)) * (d0 * d1); | ||
| 98 | + std::vector<u32> group1 = BuildRepresentativeGroup(d0, d1, level1Base + myRank % d0); | ||
| 99 | + std::vector<u32> group2 = BuildRepresentativeGroup(d0 * d1, d2, myRank % (d0 * d1)); | ||
| 100 | + CHK_RET(ValidateGroup(group0, d0, myRank, "level0")); | ||
| 101 | + CHK_RET(ValidateGroup(group1, d1, myRank, "level1")); | ||
| 102 | + CHK_RET(ValidateGroup(group2, d2, myRank, "level2")); | ||
| 103 | + algHierarchyInfo.infos.resize(ALGO_LEVEL_NUM_THREE); | ||
| 104 | + for (u32 i = 0; i < ALGO_LEVEL_NUM_THREE; i++) { | ||
| 105 | + algHierarchyInfo.infos[i].resize(1); | ||
| 106 | + } | ||
| 107 | + algHierarchyInfo.infos[0][0] = std::move(group0); | ||
| 108 | + algHierarchyInfo.infos[1][0] = std::move(group1); | ||
| 109 | + algHierarchyInfo.infos[ALGO_LEVEL_NUM_TWO][0] = std::move(group2); | ||
| 110 | + | ||
| 111 | + // 填充 physicalIdxForAlgoLevels(二级:MeshConcur 双层,普通单层) | ||
| 112 | + CHK_RET(FillPhysicalIdxForAlgoLevels(physicalLevels, effIdx, pIndices, profile.algoTypes, | ||
| 113 | + algHierarchyInfo.physicalIdxForAlgoLevels)); | ||
| 114 | + HCCL_INFO("[TopoMatchThreeLevel] Rank [%u], d0[%u] d1[%u] d2[%u].", myRank, d0, d1, d2); | ||
| 115 | + return HcclResult::HCCL_SUCCESS; | ||
| 116 | +} | ||
| 117 | + | ||
| 118 | +} // namespace ops_hccl | ||
| @@ -0,0 +1,34 @@ | |||
| 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 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | +namespace ops_hccl { | ||
| 17 | + | ||
| 18 | +class TopoMatchThreeLevel : public TopoMatchBaseV2 { | ||
| 19 | +public: | ||
| 20 | + explicit TopoMatchThreeLevel(); | ||
| 21 | + ~TopoMatchThreeLevel() override; | ||
| 22 | + | ||
| 23 | + std::string Describe() const override | ||
| 24 | + { | ||
| 25 | + return "Topo Match for Three Level Algorithm."; | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | + HcclResult MatchTopo(HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo, | ||
| 29 | + AlgHierarchyInfoForAllLevel& algHierarchyInfo, const AlgorithmProfile& profile) override; | ||
| 30 | +}; | ||
| 31 | + | ||
| 32 | +} // namespace ops_hccl | ||
| 33 | + | ||
| 34 | + | ||
| @@ -0,0 +1,114 @@ | |||
| 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 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | +namespace ops_hccl { | ||
| 16 | + | ||
| 17 | +namespace { | ||
| 18 | +// 计算内层维度 d0:LOCAL 取 localRanks.size();GLOBAL 对称取 localRanks.size(),非对称 GCD 打平 | ||
| 19 | +HcclResult CalcLevel0Dim(const PhysicalLevelInfo& level0, u32 myRank, u32& d0, bool& asymmetric, u32& gcd) | ||
| 20 | +{ | ||
| 21 | + if (level0.view == PhysicalLevelView::LOCAL) { | ||
| 22 | + d0 = static_cast<u32>(level0.localRanks.size()); | ||
| 23 | + return HcclResult::HCCL_SUCCESS; | ||
| 24 | + } | ||
| 25 | + if (level0.instSizeListByLayer.empty()) { | ||
| 26 | + HCCL_ERROR("[TopoMatchTwoLevel] netLayer [ref = %u] instSizeListByLayer is empty.", level0.ref.netLayer); | ||
| 27 | + return HcclResult::HCCL_E_INTERNAL; | ||
| 28 | + } | ||
| 29 | + if (IsInstListSymmetric(level0.instSizeListByLayer)) { | ||
| 30 | + d0 = static_cast<u32>(level0.localRanks.size()); | ||
| 31 | + asymmetric = false; | ||
| 32 | + return HcclResult::HCCL_SUCCESS; | ||
| 33 | + } | ||
| 34 | + // GLOBAL 非对称:对 instSizeListByLayer 取 GCD 打平为对称子组 | ||
| 35 | + asymmetric = true; | ||
| 36 | + gcd = CalcGcd(level0.instSizeListByLayer); | ||
| 37 | + if (gcd == 1) { | ||
| 38 | + HCCL_INFO("[TopoMatchTwoLevel] Rank [%u], asymmetric GCD=1, not support.", myRank); | ||
| 39 | + return HcclResult::HCCL_E_NOT_SUPPORT; | ||
| 40 | + } | ||
| 41 | + d0 = gcd; | ||
| 42 | + return HcclResult::HCCL_SUCCESS; | ||
| 43 | +} | ||
| 44 | + | ||
| 45 | +// 构造含 myRank 的内层组;非对称时按 gcd 从 localRanks 切子组 | ||
| 46 | +std::vector<u32> BuildLevel0Group(const PhysicalLevelInfo& level0, u32 myRank, bool asymmetric, u32 gcd) | ||
| 47 | +{ | ||
| 48 | + if (!asymmetric) { | ||
| 49 | + return level0.localRanks; | ||
| 50 | + } | ||
| 51 | + const auto& ranks = level0.localRanks; | ||
| 52 | + auto it = std::find(ranks.begin(), ranks.end(), myRank); | ||
| 53 | + if (it == ranks.end()) { | ||
| 54 | + return {}; | ||
| 55 | + } | ||
| 56 | + u32 myIdx = static_cast<u32>(it - ranks.begin()); | ||
| 57 | + u32 startIdx = (myIdx / gcd) * gcd; | ||
| 58 | + u32 endIdx = std::min(startIdx + gcd, static_cast<u32>(ranks.size())); | ||
| 59 | + return std::vector<u32>(ranks.begin() + startIdx, ranks.begin() + endIdx); | ||
| 60 | +} | ||
| 61 | +} // namespace | ||
| 62 | + | ||
| 63 | +TopoMatchTwoLevel::TopoMatchTwoLevel() {} | ||
| 64 | +TopoMatchTwoLevel::~TopoMatchTwoLevel() {} | ||
| 65 | + | ||
| 66 | +HcclResult TopoMatchTwoLevel::MatchTopo(HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo, | ||
| 67 | + AlgHierarchyInfoForAllLevel& algHierarchyInfo, const AlgorithmProfile& profile) | ||
| 68 | +{ | ||
| 69 | + (void)comm; | ||
| 70 | + const auto& physicalLevels = topoInfo->physicalLevels; | ||
| 71 | + u32 myRank = topoInfo->userRank; | ||
| 72 | + u32 userRankSize = topoInfo->userRankSize; | ||
| 73 | + if (physicalLevels.empty() || userRankSize == 0 || profile.algoTypes.size() != ALGO_LEVEL_NUM_TWO) { | ||
| 74 | + HCCL_ERROR("[TopoMatchTwoLevel] Rank [%u], invalid input.", myRank); | ||
| 75 | + return HcclResult::HCCL_E_INTERNAL; | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | + // 引擎过滤 + 锚点匹配 + 分段 + 最高层校验 | ||
| 79 | + std::vector<u32> effIdx; | ||
| 80 | + std::vector<u32> pIndices; | ||
| 81 | + CHK_RET(ResolveMapping(physicalLevels, profile, userRankSize, effIdx, pIndices)); | ||
| 82 | + u32 phys0 = effIdx[pIndices[0]]; | ||
| 83 | + | ||
| 84 | + // GCD 校验 p_0(TwoLevel 非对称打平),外层 d1 = userRankSize / d0 | ||
| 85 | + u32 d0 = 0; | ||
| 86 | + bool asymmetric = false; | ||
| 87 | + u32 gcd = 0; | ||
| 88 | + CHK_RET(CalcLevel0Dim(physicalLevels[phys0], myRank, d0, asymmetric, gcd)); | ||
| 89 | + if (d0 <= 1 || userRankSize % d0 != 0) { | ||
| 90 | + HCCL_INFO("[TopoMatchTwoLevel] userRankSize[%u] not divisible by d0[%u].", myRank, userRankSize, d0); | ||
| 91 | + return HcclResult::HCCL_E_NOT_SUPPORT; | ||
| 92 | + } | ||
| 93 | + u32 d1 = userRankSize / d0; | ||
| 94 | + | ||
| 95 | + // 构造 infos | ||
| 96 | + std::vector<u32> group0 = BuildLevel0Group(physicalLevels[phys0], myRank, asymmetric, gcd); | ||
| 97 | + std::vector<u32> group1 = BuildRepresentativeGroup(d0, d1, myRank % d0); | ||
| 98 | + CHK_RET(ValidateGroup(group0, d0, myRank, "level0")); | ||
| 99 | + CHK_RET(ValidateGroup(group1, d1, myRank, "level1")); | ||
| 100 | + algHierarchyInfo.infos.resize(ALGO_LEVEL_NUM_TWO); | ||
| 101 | + algHierarchyInfo.infos[0].resize(1); | ||
| 102 | + algHierarchyInfo.infos[1].resize(1); | ||
| 103 | + algHierarchyInfo.infos[0][0] = std::move(group0); | ||
| 104 | + algHierarchyInfo.infos[1][0] = std::move(group1); | ||
| 105 | + | ||
| 106 | + // 填充 physicalIdxForAlgoLevels(二级:MeshConcur 双层,普通单层) | ||
| 107 | + CHK_RET(FillPhysicalIdxForAlgoLevels(physicalLevels, effIdx, pIndices, profile.algoTypes, | ||
| 108 | + algHierarchyInfo.physicalIdxForAlgoLevels)); | ||
| 109 | + HCCL_INFO("[TopoMatchTwoLevel] Rank [%u], d0[%u] d1[%u] asym[%d].", myRank, d0, d1, | ||
| 110 | + static_cast<int32_t>(asymmetric)); | ||
| 111 | + return HcclResult::HCCL_SUCCESS; | ||
| 112 | +} | ||
| 113 | + | ||
| 114 | +} // namespace ops_hccl | ||
| @@ -0,0 +1,34 @@ | |||
| 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 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | +namespace ops_hccl { | ||
| 17 | + | ||
| 18 | +class TopoMatchTwoLevel : public TopoMatchBaseV2 { | ||
| 19 | +public: | ||
| 20 | + explicit TopoMatchTwoLevel(); | ||
| 21 | + ~TopoMatchTwoLevel() override; | ||
| 22 | + | ||
| 23 | + std::string Describe() const override | ||
| 24 | + { | ||
| 25 | + return "Topo Match for Two Level Algorithm."; | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | + HcclResult MatchTopo(HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo, | ||
| 29 | + AlgHierarchyInfoForAllLevel& algHierarchyInfo, const AlgorithmProfile& profile) override; | ||
| 30 | +}; | ||
| 31 | + | ||
| 32 | +} // namespace ops_hccl | ||
| 33 | + | ||
| 34 | + | ||
| @@ -17,6 +17,7 @@ | |||
| 17 | 17 | ||
| 18 | 18 | ||
| 19 | 19 | ||
| 20 | + | ||
| 20 | 21 | ||
| 21 | 22 | ||
| 22 | 23 | ||
| @@ -345,7 +346,7 @@ REGISTER_EXEC_V2( | |||
| 345 | 346 | ||
| 346 | REGISTER_EXEC_V2( | 347 | REGISTER_EXEC_V2( |
| 347 | HcclCMDType::HCCL_CMD_REDUCE_SCATTER, CcuSchedReduceScatterSoleNHRMultiLink, InsV2ReduceScatterSoleExecutor, | 348 | HcclCMDType::HCCL_CMD_REDUCE_SCATTER, CcuSchedReduceScatterSoleNHRMultiLink, InsV2ReduceScatterSoleExecutor, |
| 348 | - TopoMatch1D, CcuTempReduceScatterNhrMultiJettyMem2Mem1D); | 349 | + TopoMatchOneLevel, CcuTempReduceScatterNhrMultiJettyMem2Mem1D); |
| 349 | 350 | ||
| 350 | 351 | ||
| 351 | REGISTER_EXEC_V2( | 352 | REGISTER_EXEC_V2( |
| @@ -166,6 +166,11 @@ if(NOT HCCL_CANN_COMPAT_850) | |||
| 166 | ${CMAKE_CURRENT_SOURCE_DIR}/ops/op_common/topo/topo_match_pcie_mix.cc | 166 | ${CMAKE_CURRENT_SOURCE_DIR}/ops/op_common/topo/topo_match_pcie_mix.cc |
| 167 | ${CMAKE_CURRENT_SOURCE_DIR}/ops/op_common/topo/topo_match_3_level.cc | 167 | ${CMAKE_CURRENT_SOURCE_DIR}/ops/op_common/topo/topo_match_3_level.cc |
| 168 | ${CMAKE_CURRENT_SOURCE_DIR}/ops/op_common/topo/topo_match_squeeze_2d.cc | 168 | ${CMAKE_CURRENT_SOURCE_DIR}/ops/op_common/topo/topo_match_squeeze_2d.cc |
| 169 | + ${CMAKE_CURRENT_SOURCE_DIR}/ops/op_common/topo/topo_match_base_v2.cc | ||
| 170 | + ${CMAKE_CURRENT_SOURCE_DIR}/ops/op_common/topo/topo_match_one_level.cc | ||
| 171 | + ${CMAKE_CURRENT_SOURCE_DIR}/ops/op_common/topo/topo_match_two_level.cc | ||
| 172 | + ${CMAKE_CURRENT_SOURCE_DIR}/ops/op_common/topo/topo_match_three_level.cc | ||
| 173 | + ${CMAKE_CURRENT_SOURCE_DIR}/ops/op_common/topo/topo_match_concurrent_v2.cc | ||
| 169 | ${CMAKE_CURRENT_SOURCE_DIR}/ops/reduce_scatter/executor/ins_reduce_scatter_concurrent_executor.cc | 174 | ${CMAKE_CURRENT_SOURCE_DIR}/ops/reduce_scatter/executor/ins_reduce_scatter_concurrent_executor.cc |
| 170 | ${CMAKE_CURRENT_SOURCE_DIR}/ops/all_gather/executor/ins_v2_all_gather_concurrent_executor.cc | 175 | ${CMAKE_CURRENT_SOURCE_DIR}/ops/all_gather/executor/ins_v2_all_gather_concurrent_executor.cc |
| 171 | ${CMAKE_CURRENT_SOURCE_DIR}/ops/all_to_all_v/executor/ins_v2_all_to_all_concurrent_executor.cc | 176 | ${CMAKE_CURRENT_SOURCE_DIR}/ops/all_to_all_v/executor/ins_v2_all_to_all_concurrent_executor.cc |