已开启
[fix] change topomatch1d topomatch to onelevel #2770
acoolntly创建于 12 天前
[fix] change topomatch1d topomatch to onelevel #2770
已开启
acoolntly创建于 12 天前
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#ifndef AICPU_COMPILE101#ifndef AICPU_COMPILE
102- static const std::set<aclrtDevAttr> supportType102+ 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#endif130#endif
121 return HCCL_SUCCESS;131 return HCCL_SUCCESS;
@@ -17,6 +17,18 @@
17#include "acl_base.h"17#include "acl_base.h"
18#include "acl_rt.h"18#include "acl_rt.h"
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+#ifndef HCCL_SUPPORT_DEV_FORM_FACTOR
25+#ifdef ACL_DEVICE_FORM_FACTOR_POD
26+#define HCCL_SUPPORT_DEV_FORM_FACTOR 1
27+#else
28+#define HCCL_SUPPORT_DEV_FORM_FACTOR 0
29+#endif
30+#endif
31+ 
20namespace ops_hccl {32namespace ops_hccl {
21 33 
22#define ACLCHECK(cmd) \34#define ACLCHECK(cmd) \
@@ -38,7 +50,11 @@ haclrtGetCaptureInfo(aclrtStream stream, aclmdlRICaptureStatus& captureStatus, u
38 50 
39HcclResult haclrtGetDeviceIndexByPhyId(u32 devicePhyId, u32& deviceLogicId);51HcclResult 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 
43HcclResult LoadBinaryFromFile(59HcclResult 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#include "ins_temp_all_gather_mesh_1D_Z_axis_detour.h"14#include "ins_temp_all_gather_mesh_1D_Z_axis_detour.h"
15#include "ins_temp_all_gather_nhr.h"15#include "ins_temp_all_gather_nhr.h"
16#include "ins_temp_all_gather_nhr_dpu.h"16#include "ins_temp_all_gather_nhr_dpu.h"
17+#include "topo_match_one_level.h"
17#ifndef AICPU_COMPILE18#ifndef AICPU_COMPILE
18#include "aiv_temp_all_gather_mesh_1D.h"19#include "aiv_temp_all_gather_mesh_1D.h"
19#include "ccu_temp_all_gather_mesh_1D_mem2mem.h"20#include "ccu_temp_all_gather_mesh_1D_mem2mem.h"
@@ -319,11 +320,11 @@ REGISTER_EXEC_V2(
319 InsTempAllGatherNHR);320 InsTempAllGatherNHR);
320 321 
321REGISTER_EXEC_V2(322REGISTER_EXEC_V2(
322- HcclCMDType::HCCL_CMD_ALLGATHER, DpuAllGatherSoleNHR, InsV2AllGatherSoleExecutor, TopoMatch1D,323+ HcclCMDType::HCCL_CMD_ALLGATHER, DpuAllGatherSoleNHR, InsV2AllGatherSoleExecutor, TopoMatchOneLevel,
323 InsTempAllGatherNHRDPU);324 InsTempAllGatherNHRDPU);
324 325 
325REGISTER_EXEC_V2(326REGISTER_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#ifndef AICPU_COMPILE330#ifndef AICPU_COMPILE
@@ -361,7 +362,7 @@ REGISTER_EXEC_V2(
361#endif // !HCCL_CANN_COMPAT_850362#endif // !HCCL_CANN_COMPAT_850
362#if !defined(HCCL_CANN_COMPAT_850)363#if !defined(HCCL_CANN_COMPAT_850)
363REGISTER_EXEC_V2(364REGISTER_EXEC_V2(
364- HcclCMDType::HCCL_CMD_ALLGATHER, CcuSchedAllGatherSoleNHRMultiLink, InsV2AllGatherSoleExecutor, TopoMatch1D,365+ HcclCMDType::HCCL_CMD_ALLGATHER, CcuSchedAllGatherSoleNHRMultiLink, InsV2AllGatherSoleExecutor, TopoMatchOneLevel,
365 CcuTempAllGatherNHR1DMultiJettyMem2Mem);366 CcuTempAllGatherNHR1DMultiJettyMem2Mem);
366#endif // CANN_VERSION_NUM >= CANN_VERSION(9, 0, 0)367#endif // CANN_VERSION_NUM >= CANN_VERSION(9, 0, 0)
367 368 
@@ -115,7 +115,12 @@ InsAllReduceParallelExecutor<AlgTopoMatch, InsAlgTemplate0, InsAlgTemplate1, Ins
115{115{
116 // 使用topo match计算AlgHierarchyInfoForAllLevel116 // 使用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#if CANN_VERSION_NUM >= CANN_VERSION(9, 0, 0)1371#if CANN_VERSION_NUM >= CANN_VERSION(9, 0, 0)
1367REGISTER_EXECUTOR_BY_FOUR_TEMPS(1372REGISTER_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);
1370REGISTER_EXECUTOR_BY_FOUR_TEMPS(1375REGISTER_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);
1373REGISTER_EXECUTOR_BY_FOUR_TEMPS(1378REGISTER_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 
1377REGISTER_EXECUTOR_BY_FOUR_TEMPS(1382REGISTER_EXECUTOR_BY_FOUR_TEMPS(
@@ -1386,7 +1391,7 @@ REGISTER_EXECUTOR_BY_FOUR_TEMPS(
1386#if CANN_VERSION_NUM >= CANN_VERSION(9, 0, 0)1391#if CANN_VERSION_NUM >= CANN_VERSION(9, 0, 0)
1387REGISTER_EXECUTOR_BY_FOUR_TEMPS(1392REGISTER_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);
1391REGISTER_EXECUTOR_BY_FOUR_TEMPS(1396REGISTER_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#include "coll_alg_v2_exec_registry.h"23#include "coll_alg_v2_exec_registry.h"
24#include "topo_match_ubx.h"24#include "topo_match_ubx.h"
25#include "topo_match_base.h"25#include "topo_match_base.h"
26+#include "topo_match_base_v2.h"
27+#include "topo_match_two_level.h"
28+#include <type_traits>
26#include "topo_match_1d.h"29#include "topo_match_1d.h"
27 30 
28namespace ops_hccl {31namespace ops_hccl {
@@ -113,7 +113,12 @@ InsV2AllReduceSequenceExecutor<AlgTopoMatch, InsAlgTemplate0, InsAlgTemplate1, I
113 devType_ = topoInfo->deviceType;113 devType_ = topoInfo->deviceType;
114 // 使用topo match计算AlgHierarchyInfoForAllLevel114 // 使用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 
511REGISTER_EXECUTOR_BY_FOUR_TEMPS(516REGISTER_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_hccl520} // namespace ops_hccl
@@ -22,6 +22,9 @@
22#include "executor_v2_base.h"22#include "executor_v2_base.h"
23#include "coll_alg_v2_exec_registry.h"23#include "coll_alg_v2_exec_registry.h"
24#include "topo_match_multilevel.h"24#include "topo_match_multilevel.h"
25+#include "topo_match_base_v2.h"
26+#include "topo_match_two_level.h"
27+#include <type_traits>
25 28 
26namespace ops_hccl {29namespace ops_hccl {
27 30 
@@ -120,7 +120,12 @@ InsV2AllReduceSequenceExecutorAicpu<AlgTopoMatch, InsAlgTemplate0, InsAlgTemplat
120 rankSize_ = topoInfo->userRankSize;120 rankSize_ = topoInfo->userRankSize;
121 // 使用topo match计算AlgHierarchyInfoForAllLevel121 // 使用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#if CANN_VERSION_NUM >= CANN_VERSION(9, 0, 0)778#if CANN_VERSION_NUM >= CANN_VERSION(9, 0, 0)
774REGISTER_EXECUTOR_BY_FOUR_TEMPS(779REGISTER_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#endif // CANN_VERSION_NUM >= CANN_VERSION(9, 0, 0)783#endif // CANN_VERSION_NUM >= CANN_VERSION(9, 0, 0)
779 784 
@@ -22,6 +22,9 @@
22#include "sal.h"22#include "sal.h"
23#include "config_log.h"23#include "config_log.h"
24#include "coll_alg_v2_exec_registry.h"24#include "coll_alg_v2_exec_registry.h"
25+#include "topo_match_base_v2.h"
26+#include "topo_match_two_level.h"
27+#include <type_traits>
25 28 
26namespace ops_hccl {29namespace 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 
740REGISTER_EXEC_V2_MULTI(745REGISTER_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_hccl750} // namespace ops_hccl
@@ -22,6 +22,9 @@
22#include "executor_v2_base.h"22#include "executor_v2_base.h"
23#include "coll_alg_v2_exec_registry.h"23#include "coll_alg_v2_exec_registry.h"
24#include "topo_match_multilevel.h"24#include "topo_match_multilevel.h"
25+#include "topo_match_base_v2.h"
26+#include "topo_match_three_level.h"
27+#include <type_traits>
25 28 
26namespace ops_hccl {29namespace ops_hccl {
27 30 
@@ -69,7 +69,12 @@ HcclResult InsV2AllReduceSoleExecutor<AlgTopoMatch, InsAlgTemplate>::CalcAlgHier
69{69{
70 // 使用topo match计算AlgHierarchyInfoForAllLevel70 // 使用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#endif301#endif
297 302 
298REGISTER_EXEC_V2(303REGISTER_EXEC_V2(
299- HcclCMDType::HCCL_CMD_ALLREDUCE, AicpuAllReduceSoleMeshOneShot, InsV2AllReduceSoleExecutor, TopoMatch1D,304+ HcclCMDType::HCCL_CMD_ALLREDUCE, AicpuAllReduceSoleMeshOneShot, InsV2AllReduceSoleExecutor, TopoMatchOneLevel,
300 InsTempAllReduceMesh1DOneShot);305 InsTempAllReduceMesh1DOneShot);
301REGISTER_EXEC_V2(306REGISTER_EXEC_V2(
302- HcclCMDType::HCCL_CMD_ALLREDUCE, AicpuAllReduceSoleMeshTwoShot, InsV2AllReduceSoleExecutor, TopoMatch1D,307+ HcclCMDType::HCCL_CMD_ALLREDUCE, AicpuAllReduceSoleMeshTwoShot, InsV2AllReduceSoleExecutor, TopoMatchOneLevel,
303 InsTempAllReduceMesh1DTwoShot);308 InsTempAllReduceMesh1DTwoShot);
304REGISTER_EXEC_V2(309REGISTER_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);
310REGISTER_EXEC_V2(315REGISTER_EXEC_V2(
311- HcclCMDType::HCCL_CMD_ALLREDUCE, AicpuAllReduceSoleMeshChunkTwoShot, InsV2AllReduceSoleExecutor, TopoMatch1D,316+ HcclCMDType::HCCL_CMD_ALLREDUCE, AicpuAllReduceSoleMeshChunkTwoShot, InsV2AllReduceSoleExecutor, TopoMatchOneLevel,
312 InsTempAllReduceMesh1DTwoShotMeshChunk);317 InsTempAllReduceMesh1DTwoShotMeshChunk);
313REGISTER_EXEC_V2(318REGISTER_EXEC_V2(
314 HcclCMDType::HCCL_CMD_ALLREDUCE, AicpuAllReduceSoleNHRAicpuReduce, InsV2AllReduceSoleExecutor, TopoMatch1D,319 HcclCMDType::HCCL_CMD_ALLREDUCE, AicpuAllReduceSoleNHRAicpuReduce, InsV2AllReduceSoleExecutor, TopoMatch1D,
@@ -14,6 +14,9 @@
14#include "executor_common_ops.h"14#include "executor_common_ops.h"
15#include "topo_match_1d.h"15#include "topo_match_1d.h"
16#include "topo_match_base.h"16#include "topo_match_base.h"
17+#include "topo_match_base_v2.h"
18+#include "topo_match_one_level.h"
19+#include <type_traits>
17 20 
18namespace ops_hccl {21namespace ops_hccl {
19template <typename AlgTopoMatch, typename InsAlgTemplate>22template <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 
311REGISTER_EXECUTOR_BY_TWO_TEMPS(316REGISTER_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_hccl319} // namespace ops_hccl
@@ -14,6 +14,9 @@
14#include "executor_common_ops.h"14#include "executor_common_ops.h"
15#include "topo_match_1d.h"15#include "topo_match_1d.h"
16#include "topo_match_base.h"16#include "topo_match_base.h"
17+#include "topo_match_base_v2.h"
18+#include "topo_match_one_level.h"
19+#include <type_traits>
17 20 
18namespace ops_hccl {21namespace 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+ 
26HcclResult InsCollAlgBase::RestoreChannelMap(59HcclResult InsCollAlgBase::RestoreChannelMap(
27 const AlgResourceCtxSerializable& resCtx,60 const AlgResourceCtxSerializable& resCtx,
28 std::vector<std::map<u32, std::vector<ChannelInfo>>>& rankIdToChannelInfo) const61 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#endif84#endif
82protected:85protected:
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 // dataSize127 // 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_hccl135} // namespace ops_hccl
@@ -18,6 +18,7 @@
18#include <unordered_set>18#include <unordered_set>
19#include <memory>19#include <memory>
20#include <functional>20#include <functional>
21+#include <type_traits>
21#include <functional>22#include <functional>
22#include <memory>23#include <memory>
23#include <hccl/hccl_comm.h>24#include <hccl/hccl_comm.h>
@@ -40,6 +41,9 @@ constexpr u32 MAX_NUM_BLOCKS = 56; // 56-72
40 41 
41constexpr u32 HCCL_LOGIC_TOPO_LEVEL_NUM = 4; // HCCL逻辑拓扑层级最多4级42constexpr u32 HCCL_LOGIC_TOPO_LEVEL_NUM = 4; // HCCL逻辑拓扑层级最多4级
42 43 
44+// physicalLevels的条目数上界。定位是"超过即判定字节流不可信"的合理性阈值, 不是业务上限
45+constexpr u32 PHYSICAL_LEVEL_NUM_LIMIT = 10;
46+ 
43constexpr uint32_t DATATYPE_SIZE_TABLE[HCCL_DATA_TYPE_RESERVED]47constexpr 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#define HCCL_GROUP_NAME_MAX_LEN 127249#define HCCL_GROUP_NAME_MAX_LEN 127
184 250 
185typedef struct {251typedef struct {
@@ -211,6 +277,33 @@ struct TopoInfo {
211 bool isHCCSSWNumEqualToTwiceSIONum = false; // A3 Server内链路属性277 bool isHCCSSWNumEqualToTwiceSIONum = false; // A3 Server内链路属性
212 ThreadHandle mainThread; // 主流对应threadHandle278 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// 如果能够序列化那么就是下面的结构体
468struct AlgHierarchyInfoForAllLevel {616struct AlgHierarchyInfoForAllLevel {
469 std::vector<std::vector<std::vector<u32>>> infos; // 第一维表示有多少level,第二维是每个level的rankID617 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 // 计算AlgHierarchyInfo790 // 计算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 // 计算AlgHierarchyInfo1220 // 计算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.cc13 ${CMAKE_CURRENT_SOURCE_DIR}/topo_host.cc
14 ${CMAKE_CURRENT_SOURCE_DIR}/topo_match_1d.cc14 ${CMAKE_CURRENT_SOURCE_DIR}/topo_match_1d.cc
15 ${CMAKE_CURRENT_SOURCE_DIR}/topo_match_base.cc15 ${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.cc17 ${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)
18if(NOT HCCL_CANN_COMPAT_850)26if(NOT HCCL_CANN_COMPAT_850)
19 list(APPEND src_list27 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+#ifndef OPS_HCCL_SRC_OPS_TOPO_PHYSICAL_LEVEL
12+#define OPS_HCCL_SRC_OPS_TOPO_PHYSICAL_LEVEL
13+ 
14+#include <hccl/hccl_types.h>
15+#include <vector>
16+#include "alg_param.h"
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+#endif
@@ -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+#include "physical_level.h"
12+ 
13+#include <algorithm>
14+#include <functional>
15+#include <numeric>
16+#include <string>
17+ 
18+#include "log.h"
19+#include "hccl_rank_graph_dl.h"
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+#include "physical_level.h"
12+ 
13+#include <algorithm>
14+#include <cstring>
15+#include <functional>
16+#include <numeric>
17+ 
18+#include "log.h"
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#include "dev_type.h"23#include "dev_type.h"
24#include "dlsym_common.h"24#include "dlsym_common.h"
25#include "hccl_rank_graph_dl.h"25#include "hccl_rank_graph_dl.h"
26+#include "physical_level.h"
26 27 
27constexpr u32 FACTOR_NUM_TWO = 2;28constexpr u32 FACTOR_NUM_TWO = 2;
28constexpr s32 DEVICE_PER_MODULE = 8;29constexpr 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+#ifndef AICPU_COMPILE
782+#if !HCCL_SUPPORT_DEV_FORM_FACTOR
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+#else
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+#endif // HCCL_SUPPORT_DEV_FORM_FACTOR
825+#endif // AICPU_COMPILE
826+ return HCCL_SUCCESS;
827+}
828+ 
772HcclResult CalcTopoShape(HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo)829HcclResult 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 
95HcclResult CalcHostDPUOnly(HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo);95HcclResult CalcHostDPUOnly(HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo);
96 96 
97+/**
98+ * 查询本卡是否为POD机型并写入topoInfo->isPod。不依赖HcclComm, 只查本设备。
99+ * 恒返回HCCL_SUCCESS: 取不到时停在false, 不影响任何现有字段与执行路径。
100+ */
101+HcclResult CalcDeviceFormFactor(TopoInfoWithNetLayerDetails* topoInfo);
102+ 
97HcclResult ExtractNetLayerDetails(const HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo);103HcclResult ExtractNetLayerDetails(const HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo);
98 104 
99HcclResult ExtractTopoDetails(HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo);105HcclResult 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+#include "topo_match_base_v2.h"
12+#include <algorithm>
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+#ifndef TOPO_MATCH_BASE_V2
12+#define TOPO_MATCH_BASE_V2
13+ 
14+#include "topo_match_base.h"
15+#include <set>
16+#include <map>
17+#include <vector>
18+#include <string>
19+#include <algorithm>
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+#endif // !TOPO_MATCH_BASE_V2
@@ -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+#include "topo_match_concurrent_v2.h"
12+#include "log.h"
13+#include "hccl_common.h"
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+#ifndef TOPO_MATCH_CONCURRENT_V2
12+#define TOPO_MATCH_CONCURRENT_V2
13+ 
14+#include "topo_match_base_v2.h"
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+#endif // !TOPO_MATCH_CONCURRENT_V2
@@ -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+#include "topo_match_one_level.h"
12+#include <algorithm>
13+#include "log.h"
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+#ifndef TOPO_MATCH_ONE_LEVEL
12+#define TOPO_MATCH_ONE_LEVEL
13+ 
14+#include "topo_match_base_v2.h"
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+#endif // !TOPO_MATCH_ONE_LEVEL
@@ -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+#include "topo_match_three_level.h"
12+#include <algorithm>
13+#include "log.h"
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+#ifndef HCCLV2_TOPO_MATCH_THREE_LEVEL_H
12+#define HCCLV2_TOPO_MATCH_THREE_LEVEL_H
13+ 
14+#include "topo_match_base_v2.h"
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+#endif // !HCCLV2_TOPO_MATCH_THREE_LEVEL_H
@@ -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+#include "topo_match_two_level.h"
12+#include <algorithm>
13+#include "log.h"
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+#ifndef TOPO_MATCH_TWO_LEVEL
12+#define TOPO_MATCH_TWO_LEVEL
13+ 
14+#include "topo_match_base_v2.h"
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+#endif // !TOPO_MATCH_TWO_LEVEL
@@ -17,6 +17,7 @@
17#include "ins_temp_reduce_scatter_mesh_1D_Z_axis_detour.h"17#include "ins_temp_reduce_scatter_mesh_1D_Z_axis_detour.h"
18#include "ccu_temp_reduce_scatter_concurrent_mesh_nhr.h"18#include "ccu_temp_reduce_scatter_concurrent_mesh_nhr.h"
19#include "topo_match_concurrent.h"19#include "topo_match_concurrent.h"
20+#include "topo_match_one_level.h"
20#ifndef AICPU_COMPILE21#ifndef AICPU_COMPILE
21#if CANN_VERSION_NUM >= CANN_VERSION(9, 0, 0)22#if CANN_VERSION_NUM >= CANN_VERSION(9, 0, 0)
22#include "ccu_temp_reduce_scatter_mesh_1D_mem2mem.h"23#include "ccu_temp_reduce_scatter_mesh_1D_mem2mem.h"
@@ -345,7 +346,7 @@ REGISTER_EXEC_V2(
345#if CANN_VERSION_NUM >= CANN_VERSION(9, 0, 0)346#if CANN_VERSION_NUM >= CANN_VERSION(9, 0, 0)
346REGISTER_EXEC_V2(347REGISTER_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#endif /* CANN_VERSION_NUM >= CANN_VERSION(9, 0, 0) */350#endif /* CANN_VERSION_NUM >= CANN_VERSION(9, 0, 0) */
350#if CANN_VERSION_NUM >= CANN_VERSION(9, 0, 0)351#if CANN_VERSION_NUM >= CANN_VERSION(9, 0, 0)
351REGISTER_EXEC_V2(352REGISTER_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.cc166 ${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.cc167 ${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.cc168 ${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.cc174 ${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.cc175 ${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.cc176 ${CMAKE_CURRENT_SOURCE_DIR}/ops/all_to_all_v/executor/ins_v2_all_to_all_concurrent_executor.cc