已合并
topo parse + topo match重构 #2773
shenjiahui_创建于 10 天前
topo parse + topo match重构 #2773
已合并
共 30 个文件变更+2373-110
| @@ -96,15 +96,16 @@ HcclResult haclrtGetCaptureInfo(aclrtStream stream, aclmdlRICaptureStatus& captu | |||
| 96 | return HCCL_SUCCESS; | 96 | return HCCL_SUCCESS; |
| 97 | } | 97 | } |
| 98 | 98 | ||
| 99 | -HcclResult hcalrtGetDeviceInfo(u32 deviceId, aclrtDevAttr devAttr, s64& val) | 99 | +HcclResult hcalrtGetDeviceInfo(u32 deviceId, aclrtDevAttr devAttr, s64& val, bool quiet) |
| 100 | { | 100 | { |
| 101 | 101 | ||
| 102 | - static const std::set<aclrtDevAttr> supportType | 102 | + static const std::set<aclrtDevAttr> supportType = { |
| 103 | - = {{ACL_DEV_ATTR_PHY_CHIP_ID}, | 103 | + {ACL_DEV_ATTR_PHY_CHIP_ID}, {ACL_DEV_ATTR_SUPER_POD_DEVIDE_ID}, {ACL_DEV_ATTR_SUPER_POD_SERVER_ID}, |
| 104 | - {ACL_DEV_ATTR_SUPER_POD_DEVIDE_ID}, | 104 | + {ACL_DEV_ATTR_SUPER_POD_ID}, {ACL_DEV_ATTR_CUST_OP_PRIVILEGE}, |
| 105 | - {ACL_DEV_ATTR_SUPER_POD_SERVER_ID}, | 105 | +#if HCCL_SUPPORT_DEV_FORM_FACTOR |
| 106 | - {ACL_DEV_ATTR_SUPER_POD_ID}, | 106 | + {ACL_DEV_ATTR_DEVICE_FORM_FACTOR}, |
| 107 | - {ACL_DEV_ATTR_CUST_OP_PRIVILEGE}}; | 107 | +#endif |
| 108 | + }; | ||
| 108 | 109 | ||
| 109 | auto it = supportType.find(devAttr); | 110 | auto it = supportType.find(devAttr); |
| 110 | CHK_PRT_RET( | 111 | CHK_PRT_RET( |
| @@ -112,10 +113,19 @@ HcclResult hcalrtGetDeviceInfo(u32 deviceId, aclrtDevAttr devAttr, s64& val) | |||
| 112 | HCCL_E_NOT_SUPPORT); | 113 | HCCL_E_NOT_SUPPORT); |
| 113 | 114 | ||
| 114 | aclError ret = aclrtGetDeviceInfo(deviceId, devAttr, reinterpret_cast<int64_t*>(&val)); | 115 | aclError ret = aclrtGetDeviceInfo(deviceId, devAttr, reinterpret_cast<int64_t*>(&val)); |
| 115 | - CHK_PRT_RET( | 116 | + if (ret != ACL_SUCCESS) { |
| 116 | - ret != ACL_SUCCESS, | 117 | + // quiet用于"取不到就降级"的可选属性: 老驱动不支持某个infoType时会稳定失败, |
| 117 | - HCCL_ERROR("[hcalrtGetDeviceInfo]rt get device info failed. ret[%d], attr[%d], val[%ld]", ret, devAttr, val), | 118 | + // 按ERROR打会在完全正常的老环境上持续刷错误日志。传quiet的调用方必须自己处理返回值 |
| 118 | - HCCL_E_RUNTIME); | 119 | + if (quiet) { |
| 120 | + HCCL_WARNING( | ||
| 121 | + "[hcalrtGetDeviceInfo]rt get device info failed. ret[%d], attr[%d]. caller will fall back.", ret, | ||
| 122 | + devAttr); | ||
| 123 | + } else { | ||
| 124 | + HCCL_ERROR( | ||
| 125 | + "[hcalrtGetDeviceInfo]rt get device info failed. ret[%d], attr[%d], val[%ld]", ret, devAttr, val); | ||
| 126 | + } | ||
| 127 | + return HCCL_E_RUNTIME; | ||
| 128 | + } | ||
| 119 | HCCL_DEBUG("Call aclrtGetDeviceInfo, ret[%d], attr[%d], val[%ld]", ret, devAttr, val); | 129 | HCCL_DEBUG("Call aclrtGetDeviceInfo, ret[%d], attr[%d], val[%ld]", ret, devAttr, val); |
| 120 | 130 | ||
| 121 | return HCCL_SUCCESS; | 131 | return HCCL_SUCCESS; |
| @@ -17,6 +17,17 @@ | |||
| 17 | 17 | ||
| 18 | 18 | ||
| 19 | 19 | ||
| 20 | +/* ACL_DEV_ATTR_DEVICE_FORM_FACTOR是新版acl_rt.h才有的枚举值, 老CANN上引用会编译不过。 | ||
| 21 | + * 枚举对预处理器不可见, 因此探测与它同批引入、且只为它服务的宏ACL_DEVICE_FORM_FACTOR_POD。 | ||
| 22 | + */ | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + | ||
| 20 | namespace ops_hccl { | 31 | namespace ops_hccl { |
| 21 | 32 | ||
| 22 | 33 | ||
| @@ -38,7 +49,11 @@ haclrtGetCaptureInfo(aclrtStream stream, aclmdlRICaptureStatus& captureStatus, u | |||
| 38 | 49 | ||
| 39 | HcclResult haclrtGetDeviceIndexByPhyId(u32 devicePhyId, u32& deviceLogicId); | 50 | HcclResult haclrtGetDeviceIndexByPhyId(u32 devicePhyId, u32& deviceLogicId); |
| 40 | 51 | ||
| 41 | -HcclResult hcalrtGetDeviceInfo(u32 deviceId, aclrtDevAttr devAttr, s64& val); | 52 | +/** |
| 53 | + * @param quiet 取不到时是否降噪。默认false保持原有行为(按ERROR打); 对"取不到就走降级值"的可选属性 | ||
| 54 | + * 传true, 失败改按WARNING打。传true的调用方必须自己判返回值并给出降级值。 | ||
| 55 | + */ | ||
| 56 | +HcclResult hcalrtGetDeviceInfo(u32 deviceId, aclrtDevAttr devAttr, s64& val, bool quiet = false); | ||
| 42 | 57 | ||
| 43 | HcclResult LoadBinaryFromFile( | 58 | HcclResult LoadBinaryFromFile( |
| 44 | const char* binPath, aclrtBinaryLoadOptionType optionType, uint32_t cpuKernelMode, aclrtBinHandle& binHandle); | 59 | const char* binPath, aclrtBinaryLoadOptionType optionType, uint32_t cpuKernelMode, aclrtBinHandle& binHandle); |
| @@ -14,6 +14,7 @@ | |||
| 14 | 14 | ||
| 15 | 15 | ||
| 16 | 16 | ||
| 17 | + | ||
| 17 | 18 | ||
| 18 | 19 | ||
| 19 | 20 | ||
| @@ -21,7 +22,6 @@ | |||
| 21 | 22 | ||
| 22 | 23 | ||
| 23 | namespace ops_hccl { | 24 | namespace ops_hccl { |
| 24 | - | ||
| 25 | // --------------------------------------------------------------------------- | 25 | // --------------------------------------------------------------------------- |
| 26 | // AlgoType 枚举:算法模板类型(与 ALGO_TYPES 映射表一一对应) | 26 | // AlgoType 枚举:算法模板类型(与 ALGO_TYPES 映射表一一对应) |
| 27 | // --------------------------------------------------------------------------- | 27 | // --------------------------------------------------------------------------- |
| @@ -42,6 +42,17 @@ enum class AlgoType : uint8_t { | |||
| 42 | UNKNOWN, | 42 | UNKNOWN, |
| 43 | }; | 43 | }; |
| 44 | 44 | ||
| 45 | +// Mesh 类算法集合 | ||
| 46 | +const std::set<AlgoType> MESH_ALGO_TYPES | ||
| 47 | + = {AlgoType::MESH, AlgoType::MESH_2DIE, AlgoType::MESH_ONESHOT, | ||
| 48 | + AlgoType::MESH_TWOSHOT, AlgoType::MESH_CONCUR, AlgoType::MESH_MULTILINK, | ||
| 49 | + AlgoType::MESH_CHUNK, AlgoType::MESH_CHUNK_TWOSHOT, AlgoType::MESH_SINGLE_CHANNEL, | ||
| 50 | + AlgoType::MESH_CONCURRENT}; | ||
| 51 | +// NHR 类算法集合 | ||
| 52 | +const std::set<AlgoType> NHR_ALGO_TYPES = {AlgoType::NHR, AlgoType::NHR_MULTILINK, AlgoType::NHR_AICPU_REDUCE}; | ||
| 53 | +// MeshConcur 类算法(MESH_CONCUR 与 MESH_CONCURRENT 都触发 CLOS 双层规则) | ||
| 54 | +const std::set<AlgoType> MESH_CONCUR_ALGO_TYPES = {AlgoType::MESH_CONCUR, AlgoType::MESH_CONCURRENT}; | ||
| 55 | + | ||
| 45 | // --------------------------------------------------------------------------- | 56 | // --------------------------------------------------------------------------- |
| 46 | // 算法(template)条目 | 57 | // 算法(template)条目 |
| 47 | // algoType: 算法名称(驼峰命名),如 "mesh", "nhr", "ring", "meshMultiLink" | 58 | // algoType: 算法名称(驼峰命名),如 "mesh", "nhr", "ring", "meshMultiLink" |
| @@ -160,6 +160,40 @@ private: | |||
| 160 | std::stringstream stream; | 160 | std::stringstream stream; |
| 161 | }; | 161 | }; |
| 162 | 162 | ||
| 163 | +/* | ||
| 164 | + * 读写方向适配器。把方向从字段清单里剥离出来, 使Serialize与DeSerialize能共用同一份清单, | ||
| 165 | + * 两侧顺序不一致这类错误因此不可能再发生。用法: BinaryWriter ar(bs); ar & a & b & c; | ||
| 166 | + */ | ||
| 167 | +class BinaryWriter { | ||
| 168 | +public: | ||
| 169 | + explicit BinaryWriter(BinaryStream& stream) : stream_(stream) {} | ||
| 170 | + | ||
| 171 | + template <typename T> | ||
| 172 | + BinaryWriter& operator&(const T& t) | ||
| 173 | + { | ||
| 174 | + stream_ << t; | ||
| 175 | + return *this; | ||
| 176 | + } | ||
| 177 | + | ||
| 178 | +private: | ||
| 179 | + BinaryStream& stream_; | ||
| 180 | +}; | ||
| 181 | + | ||
| 182 | +class BinaryReader { | ||
| 183 | +public: | ||
| 184 | + explicit BinaryReader(BinaryStream& stream) : stream_(stream) {} | ||
| 185 | + | ||
| 186 | + template <typename T> | ||
| 187 | + BinaryReader& operator&(T& t) | ||
| 188 | + { | ||
| 189 | + stream_ >> t; | ||
| 190 | + return *this; | ||
| 191 | + } | ||
| 192 | + | ||
| 193 | +private: | ||
| 194 | + BinaryStream& stream_; | ||
| 195 | +}; | ||
| 196 | + | ||
| 163 | } // namespace ops_hccl | 197 | } // namespace ops_hccl |
| 164 | 198 | ||
| 165 | 199 | ||
| @@ -9,8 +9,9 @@ | |||
| 9 | */ | 9 | */ |
| 10 | 10 | ||
| 11 | 11 | ||
| 12 | -#include "topo_match_1d.h" | 12 | +#include "topo_match_one_level.h" |
| 13 | 13 | ||
| 14 | + | ||
| 14 | 15 | ||
| 15 | 16 | ||
| 16 | 17 | ||
| @@ -28,9 +29,18 @@ template <typename AlgTopoMatch, typename InsAlgTemplate> | |||
| 28 | HcclResult InsV2AllGatherVSoleExecutor<AlgTopoMatch, InsAlgTemplate>::CalcAlgHierarchyInfo( | 29 | HcclResult InsV2AllGatherVSoleExecutor<AlgTopoMatch, InsAlgTemplate>::CalcAlgHierarchyInfo( |
| 29 | HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo, AlgHierarchyInfoForAllLevel& algHierarchyInfo) | 30 | HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo, AlgHierarchyInfoForAllLevel& algHierarchyInfo) |
| 30 | { | 31 | { |
| 31 | - // 使用topo match计算AlgHierarchyInfoForAllLevel | 32 | + (void)comm; |
| 32 | AlgTopoMatch topoMatch; | 33 | AlgTopoMatch topoMatch; |
| 33 | - CHK_RET(topoMatch.MatchTopo(comm, topoInfo, algHierarchyInfo)); | 34 | + CHK_RET(topoMatch.MatchTopo(topoInfo, algHierarchyInfo, AlgAttrs{})); |
| 35 | + return HCCL_SUCCESS; | ||
| 36 | +} | ||
| 37 | + | ||
| 38 | +template <typename AlgTopoMatch, typename InsAlgTemplate> | ||
| 39 | +HcclResult InsV2AllGatherVSoleExecutor<AlgTopoMatch, InsAlgTemplate>::CalcAlgHierarchyInfoV2( | ||
| 40 | + TopoInfoWithNetLayerDetails* topoInfo, AlgHierarchyInfoForAllLevel& algHierarchyInfo, const AlgAttrs& algAttrs) | ||
| 41 | +{ | ||
| 42 | + AlgTopoMatch topoMatch; | ||
| 43 | + CHK_RET(topoMatch.MatchTopo(topoInfo, algHierarchyInfo, algAttrs)); | ||
| 34 | return HCCL_SUCCESS; | 44 | return HCCL_SUCCESS; |
| 35 | } | 45 | } |
| 36 | 46 | ||
| @@ -194,13 +204,16 @@ HcclResult InsV2AllGatherVSoleExecutor<AlgTopoMatch, InsAlgTemplate>::Orchestrat | |||
| 194 | } | 204 | } |
| 195 | 205 | ||
| 196 | REGISTER_EXEC_V2( | 206 | REGISTER_EXEC_V2( |
| 197 | - HcclCMDType::HCCL_CMD_ALLGATHER_V, AicpuAllGatherVSoleMesh, InsV2AllGatherVSoleExecutor, TopoMatch1D, | 207 | + HcclCMDType::HCCL_CMD_ALLGATHER_V, AicpuAllGatherVSoleMesh, InsV2AllGatherVSoleExecutor, TopoMatchOneLevel, |
| 198 | InsTempAllGatherVMesh1D); | 208 | InsTempAllGatherVMesh1D); |
| 209 | +REGISTER_ALG_ATTRS(AicpuAllGatherVSoleMesh, topo.maxTopoLevelNum = 3; topo.supportLevel0Topos = LEVEL0_TOPO_ANY;); | ||
| 199 | 210 | ||
| 200 | 211 | ||
| 201 | REGISTER_EXEC_V2( | 212 | REGISTER_EXEC_V2( |
| 202 | - HcclCMDType::HCCL_CMD_ALLGATHER_V, CcuSchedAllGatherVSoleMesh, InsV2AllGatherVSoleExecutor, TopoMatch1D, | 213 | + HcclCMDType::HCCL_CMD_ALLGATHER_V, CcuSchedAllGatherVSoleMesh, InsV2AllGatherVSoleExecutor, TopoMatchOneLevel, |
| 203 | CcuTempAllGatherVMesh1DMem2Mem); | 214 | CcuTempAllGatherVMesh1DMem2Mem); |
| 215 | +REGISTER_ALG_ATTRS(CcuSchedAllGatherVSoleMesh, topo.maxTopoLevelNum = 1; topo.supportLevel0Topos = LEVEL0_TOPO_MESH_1D; | ||
| 216 | + op.isSupportInplace = false;); | ||
| 204 | 217 | ||
| 205 | 218 | ||
| 206 | } // namespace ops_hccl | 219 | } // namespace ops_hccl |
| @@ -12,6 +12,7 @@ | |||
| 12 | 12 | ||
| 13 | 13 | ||
| 14 | 14 | ||
| 15 | + | ||
| 15 | 16 | ||
| 16 | namespace ops_hccl { | 17 | namespace ops_hccl { |
| 17 | template <typename AlgTopoMatch, typename InsAlgTemplate> | 18 | template <typename AlgTopoMatch, typename InsAlgTemplate> |
| @@ -31,6 +32,10 @@ public: | |||
| 31 | HcclResult CalcAlgHierarchyInfo( | 32 | HcclResult CalcAlgHierarchyInfo( |
| 32 | HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo, AlgHierarchyInfoForAllLevel& algHierarchyInfo) override; | 33 | HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo, AlgHierarchyInfoForAllLevel& algHierarchyInfo) override; |
| 33 | 34 | ||
| 35 | + HcclResult CalcAlgHierarchyInfoV2( | ||
| 36 | + TopoInfoWithNetLayerDetails* topoInfo, AlgHierarchyInfoForAllLevel& algHierarchyInfo, | ||
| 37 | + const AlgAttrs& algAttrs) override; | ||
| 38 | + | ||
| 34 | protected: | 39 | protected: |
| 35 | /* *************** 算法编排 *************** */ | 40 | /* *************** 算法编排 *************** */ |
| 36 | HcclResult OrchestrateLoop(const OpParam& param, const AlgResourceCtxSerializable& resCtx); | 41 | HcclResult OrchestrateLoop(const OpParam& param, const AlgResourceCtxSerializable& resCtx); |
| @@ -23,6 +23,38 @@ std::string InsCollAlgBase::Describe() const | |||
| 23 | return s; | 23 | return s; |
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | +CommTopo InsCollAlgBase::GetPhysicalLevelTopoType(const TopoInfoWithNetLayerDetails* topoInfo, u32 levelIdx) const | ||
| 27 | +{ | ||
| 28 | + // 不用CHK_PTR_NULL: 它返回HcclResult, 与本函数的返回类型对不上 | ||
| 29 | + if (topoInfo == nullptr) { | ||
| 30 | + HCCL_WARNING("[InsCollAlgBase][GetPhysicalLevelTopoType] topoInfo is null"); | ||
| 31 | + return CommTopo::COMM_TOPO_RESERVED; | ||
| 32 | + } | ||
| 33 | + if (levelIdx >= topoInfo->physicalLevels.size()) { | ||
| 34 | + HCCL_WARNING( | ||
| 35 | + "[InsCollAlgBase][GetPhysicalLevelTopoType] levelIdx[%u] out of range, physicalLevelNum[%zu]", levelIdx, | ||
| 36 | + topoInfo->physicalLevels.size()); | ||
| 37 | + return CommTopo::COMM_TOPO_RESERVED; | ||
| 38 | + } | ||
| 39 | + return topoInfo->physicalLevels[levelIdx].topoType; | ||
| 40 | +} | ||
| 41 | + | ||
| 42 | +std::vector<u32> | ||
| 43 | +InsCollAlgBase::GetPhysicalLevelPortNums(const TopoInfoWithNetLayerDetails* topoInfo, u32 levelIdx) const | ||
| 44 | +{ | ||
| 45 | + if (topoInfo == nullptr) { | ||
| 46 | + HCCL_WARNING("[InsCollAlgBase][GetPhysicalLevelPortNums] topoInfo is null"); | ||
| 47 | + return std::vector<u32>(); | ||
| 48 | + } | ||
| 49 | + if (levelIdx >= topoInfo->physicalLevels.size()) { | ||
| 50 | + HCCL_WARNING( | ||
| 51 | + "[InsCollAlgBase][GetPhysicalLevelPortNums] levelIdx[%u] out of range, physicalLevelNum[%zu]", levelIdx, | ||
| 52 | + topoInfo->physicalLevels.size()); | ||
| 53 | + return std::vector<u32>(); | ||
| 54 | + } | ||
| 55 | + return topoInfo->physicalLevels[levelIdx].portNums; | ||
| 56 | +} | ||
| 57 | + | ||
| 26 | HcclResult InsCollAlgBase::RestoreChannelMap( | 58 | HcclResult InsCollAlgBase::RestoreChannelMap( |
| 27 | const AlgResourceCtxSerializable& resCtx, | 59 | const AlgResourceCtxSerializable& resCtx, |
| 28 | std::vector<std::map<u32, std::vector<ChannelInfo>>>& rankIdToChannelInfo) const | 60 | std::vector<std::map<u32, std::vector<ChannelInfo>>>& rankIdToChannelInfo) const |
| @@ -57,6 +57,15 @@ public: | |||
| 57 | HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo, AlgHierarchyInfoForAllLevel& algHierarchyInfo) | 57 | HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo, AlgHierarchyInfoForAllLevel& algHierarchyInfo) |
| 58 | = 0; | 58 | = 0; |
| 59 | 59 | ||
| 60 | + virtual HcclResult CalcAlgHierarchyInfoV2( | ||
| 61 | + TopoInfoWithNetLayerDetails* topoInfo, AlgHierarchyInfoForAllLevel& algHierarchyInfo, const AlgAttrs& algAttrs) | ||
| 62 | + { | ||
| 63 | + (void)topoInfo; | ||
| 64 | + (void)algHierarchyInfo; | ||
| 65 | + (void)algAttrs; | ||
| 66 | + return HcclResult::HCCL_SUCCESS; | ||
| 67 | + } | ||
| 68 | + | ||
| 60 | virtual HcclResult CalcRes( | 69 | virtual HcclResult CalcRes( |
| 61 | HcclComm comm, const OpParam& param, const TopoInfoWithNetLayerDetails* topoInfo, | 70 | HcclComm comm, const OpParam& param, const TopoInfoWithNetLayerDetails* topoInfo, |
| 62 | const AlgHierarchyInfoForAllLevel& algHierarchyInfo, AlgResourceRequest& resourceRequest) | 71 | const AlgHierarchyInfoForAllLevel& algHierarchyInfo, AlgResourceRequest& resourceRequest) |
| @@ -95,6 +104,21 @@ public: | |||
| 95 | u32 notifyNumOnMainThread) const; | 104 | u32 notifyNumOnMainThread) const; |
| 96 | 105 | ||
| 97 | protected: | 106 | protected: |
| 107 | + /* | ||
| 108 | + * 读取physicalLevels[levelIdx]的互联形态。Level下标与netLayer编号没有固定对应关系, | ||
| 109 | + * 一个netLayer可能贡献一级或两级, 拓扑形态必须走这里回查, 不能由下标推断。 | ||
| 110 | + * 下标越界或physicalLevels为空(标准化降级)时返回COMM_TOPO_RESERVED并告警; | ||
| 111 | + * 该级无TopoInstance时字段本身就停在COMM_TOPO_RESERVED, 与前者同值, 调用方均按"形态不可用"处理。 | ||
| 112 | + */ | ||
| 113 | + CommTopo GetPhysicalLevelTopoType(const TopoInfoWithNetLayerDetails* topoInfo, u32 levelIdx) const; | ||
| 114 | + | ||
| 115 | + /* | ||
| 116 | + * 读取physicalLevels[levelIdx]上本卡各条物理链路的端口数, 降序, 按iface去重。 | ||
| 117 | + * 局部量: 同一级上各rank可能不同, 不能用它做跨rank一致的决策。 | ||
| 118 | + * 返回空统一表示"端口数不可用"(越界/无TopoInstance/采集降级), 不表示该级有0个端口。 | ||
| 119 | + */ | ||
| 120 | + std::vector<u32> GetPhysicalLevelPortNums(const TopoInfoWithNetLayerDetails* topoInfo, u32 levelIdx) const; | ||
| 121 | + | ||
| 98 | inline void SetOrderPreservedBaseParams(const OrderPreservedBaseParams& params) | 122 | inline void SetOrderPreservedBaseParams(const OrderPreservedBaseParams& params) |
| 99 | { | 123 | { |
| 100 | myRank_ = params.myRank; | 124 | myRank_ = params.myRank; |
| @@ -18,6 +18,7 @@ | |||
| 18 | 18 | ||
| 19 | 19 | ||
| 20 | 20 | ||
| 21 | + | ||
| 21 | 22 | ||
| 22 | 23 | ||
| 23 | 24 | ||
| @@ -40,6 +41,9 @@ constexpr u32 MAX_NUM_BLOCKS = 56; // 56-72 | |||
| 40 | 41 | ||
| 41 | constexpr u32 HCCL_LOGIC_TOPO_LEVEL_NUM = 4; // HCCL逻辑拓扑层级最多4级 | 42 | constexpr u32 HCCL_LOGIC_TOPO_LEVEL_NUM = 4; // HCCL逻辑拓扑层级最多4级 |
| 42 | 43 | ||
| 44 | +// physicalLevels的条目数上界。 | ||
| 45 | +constexpr u32 PHYSICAL_LEVEL_NUM_LIMIT = 10; | ||
| 46 | + | ||
| 43 | constexpr uint32_t DATATYPE_SIZE_TABLE[HCCL_DATA_TYPE_RESERVED] | 47 | constexpr uint32_t DATATYPE_SIZE_TABLE[HCCL_DATA_TYPE_RESERVED] |
| 44 | = {sizeof(int8_t), | 48 | = {sizeof(int8_t), |
| 45 | sizeof(int16_t), | 49 | sizeof(int16_t), |
| @@ -175,6 +179,58 @@ struct TopoInstDetails { | |||
| 175 | std::map<CommTopo, std::vector<u32>> rankNumForTopoType; | 179 | std::map<CommTopo, std::vector<u32>> rankNumForTopoType; |
| 176 | }; | 180 | }; |
| 177 | 181 | ||
| 182 | +// 该Level是否知道整个通信域在这个粒度上的完整划分。这是RankGraph两组接口的能力差异, 无法互相推导: | ||
| 183 | +// 只有GetInstSizeListByLayer看得到兄弟NetInstance, GetTopoInstsByLayer只看得到本rank所在的那一个 | ||
| 184 | +enum class PhysicalLevelView : u32 { | ||
| 185 | + LOCAL = 0, // 只知道当前rank所在的那一块; instSizeListByLayer恒为空 | ||
| 186 | + GLOBAL = 1, // 知道该netLayer的完整分区; instSizeListByLayer非空 | ||
| 187 | +}; | ||
| 188 | + | ||
| 189 | +// 该Level在RankGraph中的原始身份, 用于回查。 | ||
| 190 | +// netLayer恒有效(每个Level必然归属某一层); topoInstId在该Level有TopoInstance支撑时才有效 | ||
| 191 | +struct PhysicalSourceRef { | ||
| 192 | + u32 netLayer = INVALID_UINT; | ||
| 193 | + u32 topoInstId = INVALID_UINT; | ||
| 194 | +}; | ||
| 195 | + | ||
| 196 | +// 范围链上的一环。整条链按三键排序, 相邻两环的rank集合满足包含关系(可以相等) | ||
| 197 | +struct PhysicalLevelInfo { | ||
| 198 | + // 当前rank在该范围内可见的全部rank, 升序去重, 必然含当前rank。 | ||
| 199 | + // 局部量: 同一级上不同rank看到的集合不同(rank 0看到{0..7}, rank 9看到{8..15}), | ||
| 200 | + std::vector<u32> localRanks; | ||
| 201 | + PhysicalLevelView view = PhysicalLevelView::LOCAL; | ||
| 202 | + // 该netLayer上全部NetInstance的大小, 按最小rankId升序, 即一份分区布局; view为LOCAL时恒为空。 | ||
| 203 | + // 原样透传HcclRankGraphGetInstSizeListByLayer的返回序, 不重排 —— 重排会毁掉布局语义。 | ||
| 204 | + // 全局量, 跨rank逐字节相同, 是本结构唯一可用的跨rank一致性锚点 | ||
| 205 | + std::vector<u32> instSizeListByLayer; | ||
| 206 | + PhysicalSourceRef ref; | ||
| 207 | + | ||
| 208 | + // ---- 以下为链路属性: 由该Level的TopoInstance提供, 全部随hasTopoInst一起生效 ---- | ||
| 209 | + | ||
| 210 | + // 该Level有无TopoInstance支撑。false时下面全部链路属性无意义, 各自保持无效值 | ||
| 211 | + bool hasTopoInst = false; | ||
| 212 | + // 互联形态。同时是排序第三键: netLayer 0上同范围的Mesh与CLOS靠它定序 | ||
| 213 | + CommTopo topoType = CommTopo::COMM_TOPO_RESERVED; | ||
| 214 | + // 该Level的链路落在Device还是Host。消费侧据此判断"是否需要使用host网卡"(看最高一级)。 | ||
| 215 | + EndpointLocType locType = EndpointLocType::ENDPOINT_LOC_TYPE_RESERVED; | ||
| 216 | + // 该Level上出现的协议集合, 去重升序。是集合而不是单值: 同一个iface可以同时跑多种协议 | ||
| 217 | + // (如ub_ctp与ub_mem), HCOMM侧会为每种协议各生成一个EndpointDesc但它们指向同一个iface | ||
| 218 | + std::vector<CommProtocol> protocols; | ||
| 219 | + // 该Level上本卡各条物理链路的端口数, 降序, 按iface(commAddr)去重, 求和为本卡在该级的总端口数。 | ||
| 220 | + // 取自ENDPOINT_ATTR_BW_COEFF, HCOMM侧实现即iface->GetPorts().size()。 | ||
| 221 | + std::vector<u32> portNums; | ||
| 222 | + // 当前rank在该Level上的Endpoint快照, 供建链侧回查。已按(protocol, locType, addr)排序: | ||
| 223 | + // 原始返回是哈希序, 不排序会导致同一拓扑在不同进程下得到不同的字节流 | ||
| 224 | + std::vector<EndpointDesc> endpoints; | ||
| 225 | +}; | ||
| 226 | + | ||
| 227 | +/* | ||
| 228 | + * endpoints走BinaryStream的整块裸拷贝, 只对POD正确。EndpointDesc将来若引入变长成员(如std::string), | ||
| 229 | + * 写进流的会是堆指针而不是内容, 且不报错、只在远处随机崩溃。这条断言让那种改动直接编译失败。 | ||
| 230 | + */ | ||
| 231 | +static_assert( | ||
| 232 | + std::is_trivially_copyable<EndpointDesc>::value, "EndpointDesc must be trivially copyable for serialization"); | ||
| 233 | + | ||
| 178 | 234 | ||
| 179 | 235 | ||
| 180 | typedef struct { | 236 | typedef struct { |
| @@ -224,56 +280,57 @@ struct TopoInfoWithNetLayerDetails : public TopoInfo { // 通信域拓扑ctx | |||
| 224 | bool level0Symmetric{false}; | 280 | bool level0Symmetric{false}; |
| 225 | bool level1Symmetric{false}; | 281 | bool level1Symmetric{false}; |
| 226 | u32 topoInstDetailsOfLayerSize = 0; | 282 | u32 topoInstDetailsOfLayerSize = 0; |
| 283 | + // 本卡是否为POD机型, 由CalcDeviceFormFactor查ACL_DEV_ATTR_DEVICE_FORM_FACTOR得到, 取不到停在false | ||
| 284 | + bool isPod = false; | ||
| 227 | Level0MeshType level0MeshType; | 285 | Level0MeshType level0MeshType; |
| 228 | NetLayerDetails netLayerDetails; | 286 | NetLayerDetails netLayerDetails; |
| 229 | std::vector<TopoInstDetails> topoInstDetailsOfLayer; | 287 | std::vector<TopoInstDetails> topoInstDetailsOfLayer; |
| 288 | + // physicalLevels的条目数, 由Serialize统一回填。 | ||
| 289 | + u32 physicalLevelNum = 0; | ||
| 290 | + std::vector<PhysicalLevelInfo> physicalLevels; | ||
| 291 | + | ||
| 292 | + // 全部定长字段与netLayerDetails, 按声明顺序列出一次。Serialize与DeSerialize共用本清单 | ||
| 293 | + template <typename Ar> | ||
| 294 | + void VisitFields(Ar& ar) | ||
| 295 | + { | ||
| 296 | + ar & userRank & userRankSize & serverIdx & superPodIdx & deviceType & deviceNumPerModule; | ||
| 297 | + ar & serverNumPerSuperPod & serverNum & moduleNum & superPodNum & moduleIdx; | ||
| 298 | + ar & isDiffDeviceModule & multiModuleDiffDeviceNumMode & multiSuperPodDiffServerNumMode; | ||
| 299 | + ar & isHCCSSWNumEqualToTwiceSIONum & mainThread & notifyNumOnMainThread; | ||
| 300 | + ar & topoLevelNums & level0Topo & Level0Nhr & Level1Nhr & Level1Hd & is2DieFullMesh; | ||
| 301 | + ar & level0PcieMix & level0BigClosRange & topLevelUboe & level2UbRtp & hostDpuOnly; | ||
| 302 | + ar & level0Symmetric & level1Symmetric & topoInstDetailsOfLayerSize & isPod & level0MeshType; | ||
| 303 | + ar & netLayerDetails.netLayerNum & netLayerDetails.netLayers & netLayerDetails.netInstNumOfLayer; | ||
| 304 | + ar & netLayerDetails.instSizeListOfLayer & netLayerDetails.localNetInsSizeOfLayer; | ||
| 305 | + } | ||
| 306 | + | ||
| 307 | + template <typename Ar> | ||
| 308 | + static void VisitTopoInstDetails(Ar& ar, TopoInstDetails& details) | ||
| 309 | + { | ||
| 310 | + ar & details.topoInstNum & details.sizeOfTopo & details.typeOfTopo & details.ranksInTopo; | ||
| 311 | + ar & details.rankNumForTopoType; | ||
| 312 | + } | ||
| 313 | + | ||
| 314 | + template <typename Ar> | ||
| 315 | + static void VisitPhysicalLevel(Ar& ar, PhysicalLevelInfo& level) | ||
| 316 | + { | ||
| 317 | + ar & level.localRanks & level.view & level.instSizeListByLayer; | ||
| 318 | + ar & level.ref.netLayer & level.ref.topoInstId & level.hasTopoInst & level.topoType; | ||
| 319 | + ar & level.locType & level.protocols & level.portNums & level.endpoints; | ||
| 320 | + } | ||
| 230 | 321 | ||
| 231 | std::vector<char> Serialize() | 322 | std::vector<char> Serialize() |
| 232 | { | 323 | { |
| 233 | BinaryStream binaryStream; | 324 | BinaryStream binaryStream; |
| 234 | - binaryStream << userRank; | 325 | + BinaryWriter ar(binaryStream); |
| 235 | - binaryStream << userRankSize; | 326 | + VisitFields(ar); |
| 236 | - binaryStream << serverIdx; | ||
| 237 | - binaryStream << superPodIdx; | ||
| 238 | - binaryStream << deviceType; | ||
| 239 | - binaryStream << deviceNumPerModule; | ||
| 240 | - binaryStream << serverNumPerSuperPod; | ||
| 241 | - binaryStream << serverNum; | ||
| 242 | - binaryStream << moduleNum; | ||
| 243 | - binaryStream << superPodNum; | ||
| 244 | - binaryStream << moduleIdx; | ||
| 245 | - binaryStream << isDiffDeviceModule; | ||
| 246 | - binaryStream << multiModuleDiffDeviceNumMode; | ||
| 247 | - binaryStream << multiSuperPodDiffServerNumMode; | ||
| 248 | - binaryStream << isHCCSSWNumEqualToTwiceSIONum; | ||
| 249 | - binaryStream << mainThread; | ||
| 250 | - binaryStream << notifyNumOnMainThread; | ||
| 251 | - binaryStream << topoLevelNums; | ||
| 252 | - binaryStream << level0Topo; | ||
| 253 | - binaryStream << Level0Nhr; | ||
| 254 | - binaryStream << Level1Nhr; | ||
| 255 | - binaryStream << Level1Hd; | ||
| 256 | - binaryStream << is2DieFullMesh; | ||
| 257 | - binaryStream << level0PcieMix; | ||
| 258 | - binaryStream << level0BigClosRange; | ||
| 259 | - binaryStream << topLevelUboe; | ||
| 260 | - binaryStream << level2UbRtp; | ||
| 261 | - binaryStream << hostDpuOnly; | ||
| 262 | - binaryStream << level0Symmetric; | ||
| 263 | - binaryStream << level1Symmetric; | ||
| 264 | - binaryStream << topoInstDetailsOfLayerSize; | ||
| 265 | - binaryStream << level0MeshType; | ||
| 266 | - binaryStream << netLayerDetails.netLayerNum; | ||
| 267 | - binaryStream << netLayerDetails.netLayers; | ||
| 268 | - binaryStream << netLayerDetails.netInstNumOfLayer; | ||
| 269 | - binaryStream << netLayerDetails.instSizeListOfLayer; | ||
| 270 | - binaryStream << netLayerDetails.localNetInsSizeOfLayer; | ||
| 271 | for (uint32_t idx = 0; idx < topoInstDetailsOfLayerSize; idx++) { | 327 | for (uint32_t idx = 0; idx < topoInstDetailsOfLayerSize; idx++) { |
| 272 | - binaryStream << topoInstDetailsOfLayer[idx].topoInstNum; | 328 | + VisitTopoInstDetails(ar, topoInstDetailsOfLayer[idx]); |
| 273 | - binaryStream << topoInstDetailsOfLayer[idx].sizeOfTopo; | 329 | + } |
| 274 | - binaryStream << topoInstDetailsOfLayer[idx].typeOfTopo; | 330 | + physicalLevelNum = static_cast<u32>(physicalLevels.size()); |
| 275 | - binaryStream << topoInstDetailsOfLayer[idx].ranksInTopo; | 331 | + binaryStream << physicalLevelNum; |
| 276 | - binaryStream << topoInstDetailsOfLayer[idx].rankNumForTopoType; | 332 | + for (auto& level : physicalLevels) { |
| 333 | + VisitPhysicalLevel(ar, level); | ||
| 277 | } | 334 | } |
| 278 | std::vector<char> result; | 335 | std::vector<char> result; |
| 279 | binaryStream.Dump(result); | 336 | binaryStream.Dump(result); |
| @@ -283,53 +340,28 @@ struct TopoInfoWithNetLayerDetails : public TopoInfo { // 通信域拓扑ctx | |||
| 283 | void DeSerialize(std::vector<char>& data) | 340 | void DeSerialize(std::vector<char>& data) |
| 284 | { | 341 | { |
| 285 | BinaryStream binaryStream(data); | 342 | BinaryStream binaryStream(data); |
| 286 | - binaryStream >> userRank; | 343 | + BinaryReader ar(binaryStream); |
| 287 | - binaryStream >> userRankSize; | 344 | + VisitFields(ar); |
| 288 | - binaryStream >> serverIdx; | ||
| 289 | - binaryStream >> superPodIdx; | ||
| 290 | - binaryStream >> deviceType; | ||
| 291 | - binaryStream >> deviceNumPerModule; | ||
| 292 | - binaryStream >> serverNumPerSuperPod; | ||
| 293 | - binaryStream >> serverNum; | ||
| 294 | - binaryStream >> moduleNum; | ||
| 295 | - binaryStream >> superPodNum; | ||
| 296 | - binaryStream >> moduleIdx; | ||
| 297 | - binaryStream >> isDiffDeviceModule; | ||
| 298 | - binaryStream >> multiModuleDiffDeviceNumMode; | ||
| 299 | - binaryStream >> multiSuperPodDiffServerNumMode; | ||
| 300 | - binaryStream >> isHCCSSWNumEqualToTwiceSIONum; | ||
| 301 | - binaryStream >> mainThread; | ||
| 302 | - binaryStream >> notifyNumOnMainThread; | ||
| 303 | - binaryStream >> topoLevelNums; | ||
| 304 | - binaryStream >> level0Topo; | ||
| 305 | - binaryStream >> Level0Nhr; | ||
| 306 | - binaryStream >> Level1Nhr; | ||
| 307 | - binaryStream >> Level1Hd; | ||
| 308 | - binaryStream >> is2DieFullMesh; | ||
| 309 | - binaryStream >> level0PcieMix; | ||
| 310 | - binaryStream >> level0BigClosRange; | ||
| 311 | - binaryStream >> topLevelUboe; | ||
| 312 | - binaryStream >> level2UbRtp; | ||
| 313 | - binaryStream >> hostDpuOnly; | ||
| 314 | - binaryStream >> level0Symmetric; | ||
| 315 | - binaryStream >> level1Symmetric; | ||
| 316 | - binaryStream >> topoInstDetailsOfLayerSize; | ||
| 317 | - binaryStream >> level0MeshType; | ||
| 318 | - binaryStream >> netLayerDetails.netLayerNum; | ||
| 319 | - binaryStream >> netLayerDetails.netLayers; | ||
| 320 | - binaryStream >> netLayerDetails.netInstNumOfLayer; | ||
| 321 | - binaryStream >> netLayerDetails.instSizeListOfLayer; | ||
| 322 | - binaryStream >> netLayerDetails.localNetInsSizeOfLayer; | ||
| 323 | if (topoInstDetailsOfLayerSize > HCCL_LOGIC_TOPO_LEVEL_NUM) { | 345 | if (topoInstDetailsOfLayerSize > HCCL_LOGIC_TOPO_LEVEL_NUM) { |
| 324 | topoInstDetailsOfLayerSize = HCCL_LOGIC_TOPO_LEVEL_NUM; | 346 | topoInstDetailsOfLayerSize = HCCL_LOGIC_TOPO_LEVEL_NUM; |
| 325 | } | 347 | } |
| 326 | topoInstDetailsOfLayer.resize(topoInstDetailsOfLayerSize); | 348 | topoInstDetailsOfLayer.resize(topoInstDetailsOfLayerSize); |
| 327 | for (uint32_t idx = 0; idx < topoInstDetailsOfLayerSize; idx++) { | 349 | for (uint32_t idx = 0; idx < topoInstDetailsOfLayerSize; idx++) { |
| 328 | - binaryStream >> topoInstDetailsOfLayer[idx].topoInstNum; | 350 | + VisitTopoInstDetails(ar, topoInstDetailsOfLayer[idx]); |
| 329 | - binaryStream >> topoInstDetailsOfLayer[idx].sizeOfTopo; | 351 | + } |
| 330 | - binaryStream >> topoInstDetailsOfLayer[idx].typeOfTopo; | 352 | + physicalLevelNum = 0; |
| 331 | - binaryStream >> topoInstDetailsOfLayer[idx].ranksInTopo; | 353 | + physicalLevels.clear(); |
| 332 | - binaryStream >> topoInstDetailsOfLayer[idx].rankNumForTopoType; | 354 | + binaryStream >> physicalLevelNum; |
| 355 | + if (physicalLevelNum > PHYSICAL_LEVEL_NUM_LIMIT) { | ||
| 356 | + HCCL_WARNING( | ||
| 357 | + "[TopoInfo][DeSerialize] implausible physicalLevelNum[%u], drop the whole physical level section", | ||
| 358 | + physicalLevelNum); | ||
| 359 | + physicalLevelNum = 0; | ||
| 360 | + return; | ||
| 361 | + } | ||
| 362 | + physicalLevels.resize(physicalLevelNum); | ||
| 363 | + for (auto& level : physicalLevels) { | ||
| 364 | + VisitPhysicalLevel(ar, level); | ||
| 333 | } | 365 | } |
| 334 | } | 366 | } |
| 335 | }; | 367 | }; |
| @@ -459,9 +491,24 @@ struct AlgResourceCtx { | |||
| 459 | // ChannelInfo* channels; // 通信链路,数量可根据algHierarchyInfo字段进行推算 | 491 | // ChannelInfo* channels; // 通信链路,数量可根据algHierarchyInfo字段进行推算 |
| 460 | }; | 492 | }; |
| 461 | 493 | ||
| 494 | +// 物理层索引,用于 physicalIdxForAlgoLevels | ||
| 495 | +enum class PhysicalLevelIndex : uint32_t { | ||
| 496 | + PHYSICAL_LEVEL_IDX_0, | ||
| 497 | + PHYSICAL_LEVEL_IDX_1, | ||
| 498 | + PHYSICAL_LEVEL_IDX_2, | ||
| 499 | + PHYSICAL_LEVEL_IDX_3, | ||
| 500 | + PHYSICAL_LEVEL_IDX_4, | ||
| 501 | + PHYSICAL_LEVEL_IDX_5, | ||
| 502 | + PHYSICAL_LEVEL_IDX_6, | ||
| 503 | + PHYSICAL_LEVEL_IDX_7, | ||
| 504 | + PHYSICAL_LEVEL_IDX_8, | ||
| 505 | + PHYSICAL_LEVEL_IDX_9, | ||
| 506 | +}; | ||
| 507 | + | ||
| 462 | // 如果能够序列化那么就是下面的结构体 | 508 | // 如果能够序列化那么就是下面的结构体 |
| 463 | struct AlgHierarchyInfoForAllLevel { | 509 | struct AlgHierarchyInfoForAllLevel { |
| 464 | std::vector<std::vector<std::vector<u32>>> infos; // 第一维表示有多少level,第二维是每个level的rankID | 510 | std::vector<std::vector<std::vector<u32>>> infos; // 第一维表示有多少level,第二维是每个level的rankID |
| 511 | + std::vector<std::vector<PhysicalLevelIndex>> physicalIdxForAlgoLevels; // 每个算法层可对应多个物理层 | ||
| 465 | }; | 512 | }; |
| 466 | // 如果能够序列化那么就是下面的结构体 | 513 | // 如果能够序列化那么就是下面的结构体 |
| 467 | // 先序列化,把东西考到device,然后把指针存到OpParam,在device侧反序列该指针执行的内存 | 514 | // 先序列化,把东西考到device,然后把指针存到OpParam,在device侧反序列该指针执行的内存 |
| @@ -497,6 +544,7 @@ struct AlgResourceCtxSerializable { | |||
| 497 | 544 | ||
| 498 | binaryStream << algType; | 545 | binaryStream << algType; |
| 499 | binaryStream << algHierarchyInfo.infos; | 546 | binaryStream << algHierarchyInfo.infos; |
| 547 | + binaryStream << algHierarchyInfo.physicalIdxForAlgoLevels; | ||
| 500 | binaryStream << cclMem; | 548 | binaryStream << cclMem; |
| 501 | binaryStream << notifyNumOnMainThread; | 549 | binaryStream << notifyNumOnMainThread; |
| 502 | binaryStream << slaveThreadNum; | 550 | binaryStream << slaveThreadNum; |
| @@ -532,6 +580,7 @@ struct AlgResourceCtxSerializable { | |||
| 532 | 580 | ||
| 533 | binaryStream >> algType; | 581 | binaryStream >> algType; |
| 534 | binaryStream >> algHierarchyInfo.infos; | 582 | binaryStream >> algHierarchyInfo.infos; |
| 583 | + binaryStream >> algHierarchyInfo.physicalIdxForAlgoLevels; | ||
| 535 | binaryStream >> cclMem; | 584 | binaryStream >> cclMem; |
| 536 | binaryStream >> notifyNumOnMainThread; | 585 | binaryStream >> notifyNumOnMainThread; |
| 537 | binaryStream >> slaveThreadNum; | 586 | binaryStream >> slaveThreadNum; |
| @@ -793,7 +793,12 @@ HcclResult GeReuseResource( | |||
| 793 | { | 793 | { |
| 794 | // 计算AlgHierarchyInfo | 794 | // 计算AlgHierarchyInfo |
| 795 | AlgHierarchyInfoForAllLevel algHierarchyInfo; // 分级通信域信息{localRankId, localRankSize} | 795 | AlgHierarchyInfoForAllLevel algHierarchyInfo; // 分级通信域信息{localRankId, localRankSize} |
| 796 | - CHK_RET(executor->CalcAlgHierarchyInfo(comm, topoInfo, algHierarchyInfo)); | 796 | + if (param.opType == HcclCMDType::HCCL_CMD_ALLGATHER_V || param.opType == HcclCMDType::HCCL_CMD_REDUCE_SCATTER_V) { |
| 797 | + AlgAttrs algAttrs = executor->GetAlgoMeta(std::string(param.algName)); | ||
| 798 | + CHK_RET(executor->CalcAlgHierarchyInfoV2(topoInfo, algHierarchyInfo, algAttrs)); | ||
| 799 | + } else { | ||
| 800 | + CHK_RET(executor->CalcAlgHierarchyInfo(comm, topoInfo, algHierarchyInfo)); | ||
| 801 | + } | ||
| 797 | // 资源计算 | 802 | // 资源计算 |
| 798 | AlgResourceRequest resRequest; | 803 | AlgResourceRequest resRequest; |
| 799 | CHK_RET(executor->CalcRes(comm, param, topoInfo, algHierarchyInfo, resRequest)); | 804 | CHK_RET(executor->CalcRes(comm, param, topoInfo, algHierarchyInfo, resRequest)); |
| @@ -1228,7 +1233,11 @@ HcclResult HcclGetAlgRes( | |||
| 1228 | 1233 | ||
| 1229 | // 计算AlgHierarchyInfo | 1234 | // 计算AlgHierarchyInfo |
| 1230 | AlgHierarchyInfoForAllLevel algHierarchyInfo; // 分级通信域信息{localRankId, localRankSize} | 1235 | AlgHierarchyInfoForAllLevel algHierarchyInfo; // 分级通信域信息{localRankId, localRankSize} |
| 1231 | - CHK_RET(executor->CalcAlgHierarchyInfo(comm, topoInfo, algHierarchyInfo)); | 1236 | + if (param.opType == HcclCMDType::HCCL_CMD_ALLGATHER_V || param.opType == HcclCMDType::HCCL_CMD_REDUCE_SCATTER_V) { |
| 1237 | + CHK_RET(executor->CalcAlgHierarchyInfoV2(topoInfo, algHierarchyInfo, algoMeta)); | ||
| 1238 | + } else { | ||
| 1239 | + CHK_RET(executor->CalcAlgHierarchyInfo(comm, topoInfo, algHierarchyInfo)); | ||
| 1240 | + } | ||
| 1232 | // 资源计算 | 1241 | // 资源计算 |
| 1233 | HCCL_INFO("[HcclGetAlgRes] executor->CalcRes."); | 1242 | HCCL_INFO("[HcclGetAlgRes] executor->CalcRes."); |
| 1234 | AlgResourceRequest resRequest; | 1243 | AlgResourceRequest resRequest; |
| @@ -13,7 +13,15 @@ set(src_list | |||
| 13 | ${CMAKE_CURRENT_SOURCE_DIR}/topo_host.cc | 13 | ${CMAKE_CURRENT_SOURCE_DIR}/topo_host.cc |
| 14 | ${CMAKE_CURRENT_SOURCE_DIR}/topo_match_1d.cc | 14 | ${CMAKE_CURRENT_SOURCE_DIR}/topo_match_1d.cc |
| 15 | ${CMAKE_CURRENT_SOURCE_DIR}/topo_match_base.cc | 15 | ${CMAKE_CURRENT_SOURCE_DIR}/topo_match_base.cc |
| 16 | + ${CMAKE_CURRENT_SOURCE_DIR}/topo_match_base_v2.cc | ||
| 16 | ${CMAKE_CURRENT_SOURCE_DIR}/topo_match_concurrent.cc | 17 | ${CMAKE_CURRENT_SOURCE_DIR}/topo_match_concurrent.cc |
| 18 | + # PhysicalLevel标准化: 不依赖版本宏, 因此放在无条件块 | ||
| 19 | + ${CMAKE_CURRENT_SOURCE_DIR}/physical_level_build.cc | ||
| 20 | + ${CMAKE_CURRENT_SOURCE_DIR}/physical_level_normalize.cc | ||
| 21 | + ${CMAKE_CURRENT_SOURCE_DIR}/topo_match_one_level.cc | ||
| 22 | + ${CMAKE_CURRENT_SOURCE_DIR}/topo_match_two_level.cc | ||
| 23 | + ${CMAKE_CURRENT_SOURCE_DIR}/topo_match_three_level.cc | ||
| 24 | + ${CMAKE_CURRENT_SOURCE_DIR}/topo_match_concurrent_v2.cc | ||
| 17 | ) | 25 | ) |
| 18 | if(NOT HCCL_CANN_COMPAT_850) | 26 | if(NOT HCCL_CANN_COMPAT_850) |
| 19 | list(APPEND src_list | 27 | list(APPEND src_list |
| @@ -0,0 +1,64 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | +namespace ops_hccl { | ||
| 19 | + | ||
| 20 | +// Endpoint数量的合理性阈值, 不是防御性截断: 该值是纯本地量(接口数 x 协议数), 不随rankSize增长, | ||
| 21 | +// 超过该量级只可能是HCOMM侧异常, 此时局部降级并告警 | ||
| 22 | +constexpr u32 ENDPOINT_NUM_SANITY_LIMIT = 64; | ||
| 23 | + | ||
| 24 | +// 单个端口组的端口数合理性阈值。HCOMM侧MAX_PORT_NUM是32, 驱动侧UB口上限36, | ||
| 25 | +// 超过该量级说明ENDPOINT_ATTR_BW_COEFF返回的不是端口数, 整个Level的portNums不可信 | ||
| 26 | +constexpr u32 PORT_NUM_SANITY_LIMIT = 64; | ||
| 27 | + | ||
| 28 | +// ---- 纯函数: 不依赖HcclComm与RankGraph, 可离线UT (physical_level_normalize.cc) ---- | ||
| 29 | + | ||
| 30 | +/** | ||
| 31 | + * EndpointDesc的稳定排序键。GetEndpointDesc的输出是哈希序, 必须归一化后再保存。 | ||
| 32 | + * 按字段比较而不是memcmp整个结构体: 尾部raws在HCOMM侧从未赋值。 | ||
| 33 | + */ | ||
| 34 | +bool EndpointDescLess(const EndpointDesc& lhs, const EndpointDesc& rhs); | ||
| 35 | + | ||
| 36 | +/** | ||
| 37 | + * 两个EndpointDesc是否指向同一个iface。判据是commAddr —— HCOMM的endpointToIfaceMap以 | ||
| 38 | + * (commAddr, protocol)为键, 同addr不同protocol必然映射到同一个iface。用于按链路统计端口数。 | ||
| 39 | + */ | ||
| 40 | +bool CommAddrEqual(const CommAddr& lhs, const CommAddr& rhs); | ||
| 41 | + | ||
| 42 | +/** | ||
| 43 | + * 候选范围的标准化: 归一 -> 三键排序 -> 范围链校验, 不做合并。candidates按值语义被移动消耗。 | ||
| 44 | + * 返回HCCL_E_NOT_SUPPORT表示不构成范围链或排序键取不到值, 由调用方降级。 | ||
| 45 | + */ | ||
| 46 | +HcclResult NormalizePhysicalLevels( | ||
| 47 | + std::vector<PhysicalLevelInfo>& candidates, u32 userRank, u32 userRankSize, std::vector<PhysicalLevelInfo>& levels); | ||
| 48 | + | ||
| 49 | +/** | ||
| 50 | + * 标准化结果的一致性校验, 逐条对应标准化后应当成立的不变量。 | ||
| 51 | + */ | ||
| 52 | +HcclResult ValidatePhysicalLevels(const std::vector<PhysicalLevelInfo>& levels, u32 userRank, u32 userRankSize); | ||
| 53 | + | ||
| 54 | +// ---- 依赖HcclComm (physical_level_build.cc) ---- | ||
| 55 | + | ||
| 56 | +/** | ||
| 57 | + * 构建topoInfo->physicalLevels。任何失败一律降级为空视图并返回HCCL_SUCCESS, | ||
| 58 | + * 绝不改变CalcTopoShape的返回值。 | ||
| 59 | + */ | ||
| 60 | +HcclResult BuildPhysicalLevels(HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo); | ||
| 61 | + | ||
| 62 | +} // namespace ops_hccl | ||
| 63 | + | ||
| 64 | + | ||
| @@ -0,0 +1,495 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | +namespace ops_hccl { | ||
| 22 | +namespace { | ||
| 23 | + | ||
| 24 | + // 日志里一个vector最多展开的元素数, 超出部分省略 | ||
| 25 | + constexpr size_t LOG_VEC_MAX_ITEM = 16; | ||
| 26 | + | ||
| 27 | + std::string VecToStr(const std::vector<u32>& vec) | ||
| 28 | + { | ||
| 29 | + std::string str; | ||
| 30 | + for (size_t i = 0; i < vec.size(); ++i) { | ||
| 31 | + if (i >= LOG_VEC_MAX_ITEM) { | ||
| 32 | + str += ",..."; | ||
| 33 | + break; | ||
| 34 | + } | ||
| 35 | + if (i > 0) { | ||
| 36 | + str += ","; | ||
| 37 | + } | ||
| 38 | + str += std::to_string(vec[i]); | ||
| 39 | + } | ||
| 40 | + return str; | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + // 把一个Level拼成一行日志。localRanks只打首尾与个数, 不整条展开: 顶层那一级等于整个通信域, | ||
| 44 | + // 万卡场景整条打出来没人看得完, 而升序与含myRank由校验侧保证 | ||
| 45 | + std::string DescribeLevel(const PhysicalLevelInfo& level, size_t idx, size_t total) | ||
| 46 | + { | ||
| 47 | + return "level[" + std::to_string(idx) + "/" + std::to_string(total) + "] rankNum[" | ||
| 48 | + + std::to_string(level.localRanks.size()) + "] ranks[" | ||
| 49 | + + (level.localRanks.empty() ? | ||
| 50 | + std::string("-") : | ||
| 51 | + std::to_string(level.localRanks.front()) + ".." + std::to_string(level.localRanks.back())) | ||
| 52 | + + "] view[" + std::to_string(static_cast<u32>(level.view)) + "] instSizeListByLayer[" | ||
| 53 | + + VecToStr(level.instSizeListByLayer) + "] ref[layer " + std::to_string(level.ref.netLayer) + " inst " | ||
| 54 | + + std::to_string(level.ref.topoInstId) + "] hasTopoInst[" + std::to_string(level.hasTopoInst ? 1 : 0) | ||
| 55 | + + "] topoType[" + std::to_string(static_cast<s32>(level.topoType)) + "] locType[" | ||
| 56 | + + std::to_string(static_cast<s32>(level.locType)) + "] protocolNum[" | ||
| 57 | + + std::to_string(level.protocols.size()) + "] portNums[" + VecToStr(level.portNums) + "]"; | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + /** | ||
| 61 | + * 提取当前rank在指定TopoInstance上的Endpoint快照。 | ||
| 62 | + * 返回void: endpoints是payload叶子, 不参与排序键与范围链结构, 全部失败路径都局部降级为空。 | ||
| 63 | + */ | ||
| 64 | + void FetchEndpoints(HcclComm comm, u32 layer, u32 instId, std::vector<EndpointDesc>& out) | ||
| 65 | + { | ||
| 66 | + out.clear(); | ||
| 67 | + u32 num = 0; | ||
| 68 | + // 与下面的num == 0分开判: 取数失败是真异常, 需要留日志 | ||
| 69 | + if (HcclRankGraphGetEndpointNum(comm, layer, instId, &num) != HCCL_SUCCESS) { | ||
| 70 | + HCCL_WARNING( | ||
| 71 | + "[PhysicalLevel][Build] get endpoint num failed at layer[%u] inst[%u], skip endpoints", layer, instId); | ||
| 72 | + return; | ||
| 73 | + } | ||
| 74 | + // 0是合法结果, 不是错误: 当前rank在该topoInst上没有接口/协议时就是0 | ||
| 75 | + if (num == 0) { | ||
| 76 | + HCCL_DEBUG("[PhysicalLevel][Build] no endpoint at layer[%u] inst[%u]", layer, instId); | ||
| 77 | + return; | ||
| 78 | + } | ||
| 79 | + // 合理性阈值, 不截断: 截断只会把异常掩盖成"正常但数据少", 直接局部降级并告警 | ||
| 80 | + if (num > ENDPOINT_NUM_SANITY_LIMIT) { | ||
| 81 | + HCCL_WARNING( | ||
| 82 | + "[PhysicalLevel][Build] implausible endpoint num[%u] at layer[%u] inst[%u], skip endpoints", num, layer, | ||
| 83 | + instId); | ||
| 84 | + return; | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + // num是实际写入条数的上界(GetEndpointNum求和时不去重), 必须以回写的descNum为准resize | ||
| 88 | + std::vector<EndpointDesc> buf(num); | ||
| 89 | + u32 actualNum = num; | ||
| 90 | + if (HcclRankGraphGetEndpointDesc(comm, layer, instId, &actualNum, buf.data()) != HCCL_SUCCESS) { | ||
| 91 | + HCCL_WARNING( | ||
| 92 | + "[PhysicalLevel][Build] get endpoint desc failed at layer[%u] inst[%u], skip endpoints", layer, instId); | ||
| 93 | + return; | ||
| 94 | + } | ||
| 95 | + if (actualNum > num) { | ||
| 96 | + HCCL_WARNING( | ||
| 97 | + "[PhysicalLevel][Build] endpoint descNum[%u] exceeds requested[%u] at layer[%u] inst[%u], skip " | ||
| 98 | + "endpoints", | ||
| 99 | + actualNum, num, layer, instId); | ||
| 100 | + return; | ||
| 101 | + } | ||
| 102 | + buf.resize(actualNum); | ||
| 103 | + // GetEndpointDesc的输出顺序是unordered_map哈希序, 必须归一化后再保存 | ||
| 104 | + std::sort(buf.begin(), buf.end(), EndpointDescLess); | ||
| 105 | + out = std::move(buf); | ||
| 106 | + } | ||
| 107 | + | ||
| 108 | + /** | ||
| 109 | + * 采集本rank在该Level上各条物理链路的端口数, 降序写入out。按iface(commAddr)去重, 一条链路一项: | ||
| 110 | + * 一个iface有N种协议就有N个EndpointDesc, 逐endpoint查会把同一条链路的端口数重复计入。 | ||
| 111 | + * 全有或全无: 任一条取不到就整个清空 —— 残缺数组会让消费侧算出"看着合理但偏小"的总端口数。 | ||
| 112 | + */ | ||
| 113 | + void FetchPortNums(HcclComm comm, u32 myRank, const std::vector<EndpointDesc>& endpoints, std::vector<u32>& out) | ||
| 114 | + { | ||
| 115 | + out.clear(); | ||
| 116 | + // 空有两种来源: 本就没有接口, 或FetchEndpoints已降级(含HCOMM低版本弱符号未命中), | ||
| 117 | + // 因此这里不需要再做一次能力探测 | ||
| 118 | + if (endpoints.empty()) { | ||
| 119 | + return; | ||
| 120 | + } | ||
| 121 | + | ||
| 122 | + std::vector<CommAddr> seenAddrs; // 已计入的iface。规模是个位数, 线性查找即可 | ||
| 123 | + std::vector<u32> portNums; | ||
| 124 | + for (const auto& desc : endpoints) { | ||
| 125 | + bool seen = false; | ||
| 126 | + for (const auto& addr : seenAddrs) { | ||
| 127 | + if (CommAddrEqual(addr, desc.commAddr)) { | ||
| 128 | + seen = true; | ||
| 129 | + break; | ||
| 130 | + } | ||
| 131 | + } | ||
| 132 | + if (seen) { | ||
| 133 | + continue; // 同一个iface的另一种协议, 端口数已经计过 | ||
| 134 | + } | ||
| 135 | + | ||
| 136 | + EndpointAttrBwCoeff portNum{}; | ||
| 137 | + // ENDPOINT_ATTR_BW_COEFF名为"带宽系数", HCOMM侧实现即iface->GetPorts().size() | ||
| 138 | + if (HcclRankGraphGetEndpointInfo( | ||
| 139 | + comm, myRank, &desc, ENDPOINT_ATTR_BW_COEFF, sizeof(EndpointAttrBwCoeff), &portNum) | ||
| 140 | + != HCCL_SUCCESS) { | ||
| 141 | + HCCL_WARNING( | ||
| 142 | + "[PhysicalLevel][Build] get port num failed for rank[%u] protocol[%d], drop port nums of this " | ||
| 143 | + "level", | ||
| 144 | + myRank, static_cast<s32>(desc.protocol)); | ||
| 145 | + return; | ||
| 146 | + } | ||
| 147 | + // 0与超限都判为不可信, 口径对齐op_common.cc的BuildChannelInfo | ||
| 148 | + if (portNum == 0 || portNum > PORT_NUM_SANITY_LIMIT) { | ||
| 149 | + HCCL_WARNING( | ||
| 150 | + "[PhysicalLevel][Build] implausible port num[%u] for rank[%u] protocol[%d], drop port nums of " | ||
| 151 | + "this level", | ||
| 152 | + portNum, myRank, static_cast<s32>(desc.protocol)); | ||
| 153 | + return; | ||
| 154 | + } | ||
| 155 | + seenAddrs.push_back(desc.commAddr); | ||
| 156 | + portNums.push_back(static_cast<u32>(portNum)); | ||
| 157 | + } | ||
| 158 | + // 降序。与endpoints的(protocol, locType, addr)序无关, 两者是同一批iface的两种独立排列 | ||
| 159 | + std::sort(portNums.begin(), portNums.end(), std::greater<u32>()); | ||
| 160 | + out = std::move(portNums); | ||
| 161 | + } | ||
| 162 | + | ||
| 163 | + /** | ||
| 164 | + * 从endpoints提炼该Level的位置与协议集合。locType各endpoint不一致时置RESERVED并告警: | ||
| 165 | + * 一个Level对应一种网络平面, 位置本应唯一, 给出任一个都会误导"是否需要host网卡"的判断。 | ||
| 166 | + */ | ||
| 167 | + void FetchLocAndProtocols( | ||
| 168 | + const std::vector<EndpointDesc>& endpoints, EndpointLocType& locType, std::vector<CommProtocol>& protocols) | ||
| 169 | + { | ||
| 170 | + locType = EndpointLocType::ENDPOINT_LOC_TYPE_RESERVED; | ||
| 171 | + protocols.clear(); | ||
| 172 | + if (endpoints.empty()) { | ||
| 173 | + return; | ||
| 174 | + } | ||
| 175 | + | ||
| 176 | + locType = endpoints.front().loc.locType; | ||
| 177 | + for (const auto& desc : endpoints) { | ||
| 178 | + if (desc.loc.locType != locType) { | ||
| 179 | + HCCL_WARNING( | ||
| 180 | + "[PhysicalLevel][Build] mixed endpoint locType[%d] vs [%d] on one level, mark location unknown", | ||
| 181 | + static_cast<s32>(desc.loc.locType), static_cast<s32>(locType)); | ||
| 182 | + locType = EndpointLocType::ENDPOINT_LOC_TYPE_RESERVED; | ||
| 183 | + break; | ||
| 184 | + } | ||
| 185 | + } | ||
| 186 | + | ||
| 187 | + protocols.reserve(endpoints.size()); | ||
| 188 | + for (const auto& desc : endpoints) { | ||
| 189 | + protocols.push_back(desc.protocol); | ||
| 190 | + } | ||
| 191 | + // 去重升序: endpoints已按protocol为首键排过, 但同一协议可能出现在多个iface上 | ||
| 192 | + std::sort(protocols.begin(), protocols.end()); | ||
| 193 | + protocols.erase(std::unique(protocols.begin(), protocols.end()), protocols.end()); | ||
| 194 | + } | ||
| 195 | + | ||
| 196 | + // 取该layer本地NetInstance的rank集合, 并与netLayerDetails做跨调用一致性校验 | ||
| 197 | + HcclResult | ||
| 198 | + FetchLocalNetRanks(HcclComm comm, const NetLayerDetails& details, u32 layer, u32 myRank, std::vector<u32>& ranks) | ||
| 199 | + { | ||
| 200 | + u32* rawRanks = nullptr; | ||
| 201 | + u32 rankNum = 0; | ||
| 202 | + if (HcclRankGraphGetRanksByLayer(comm, layer, &rawRanks, &rankNum) != HCCL_SUCCESS || rawRanks == nullptr) { | ||
| 203 | + HCCL_WARNING("[PhysicalLevel][Build] get ranks by layer[%u] failed", layer); | ||
| 204 | + return HCCL_E_INTERNAL; | ||
| 205 | + } | ||
| 206 | + // HCOMM为该接口只持有一个成员vector, 下一次调用会clear()并重填它, 必须立即复制 | ||
| 207 | + ranks.assign(rawRanks, rawRanks + rankNum); | ||
| 208 | + | ||
| 209 | + // 跨调用一致性校验: localNetInsSizeOfLayer来自ExtractNetLayerDetails中的另一次调用, | ||
| 210 | + // 与此处不同源, 不一致说明RankGraph在两次调用之间发生了变化 | ||
| 211 | + if (ranks.size() != details.localNetInsSizeOfLayer[layer]) { | ||
| 212 | + HCCL_WARNING( | ||
| 213 | + "[PhysicalLevel][Build] netLayer[%u] rankNum[%zu] mismatches localNetInsSize[%u]", layer, ranks.size(), | ||
| 214 | + details.localNetInsSizeOfLayer[layer]); | ||
| 215 | + return HCCL_E_INTERNAL; | ||
| 216 | + } | ||
| 217 | + if (std::find(ranks.begin(), ranks.end(), myRank) == ranks.end()) { | ||
| 218 | + HCCL_WARNING( | ||
| 219 | + "[PhysicalLevel][Build] netLayer[%u] local instance does not contain myRank[%u]", layer, myRank); | ||
| 220 | + return HCCL_E_INTERNAL; | ||
| 221 | + } | ||
| 222 | + return HCCL_SUCCESS; | ||
| 223 | + } | ||
| 224 | + | ||
| 225 | + // 校验分区布局: 非空、总和等于通信域规模, 且用myRank做前缀和能定位到大小正确的那一块 | ||
| 226 | + HcclResult ValidateInstSizeLayout( | ||
| 227 | + const std::vector<u32>& instSizeList, u32 layer, u32 myRank, size_t localRankNum, u32 userRankSize) | ||
| 228 | + { | ||
| 229 | + if (instSizeList.empty()) { | ||
| 230 | + HCCL_WARNING("[PhysicalLevel][Build] netLayer[%u] inst size list is empty", layer); | ||
| 231 | + return HCCL_E_INTERNAL; | ||
| 232 | + } | ||
| 233 | + // 哨兵, 正常路径永不触发(ExtractNetLayerDetails已用同一等式先行校验过)。 | ||
| 234 | + // 保留它只为在HCOMM改变分层语义时第一时间暴露 | ||
| 235 | + const u32 totalRankNum = std::accumulate(instSizeList.begin(), instSizeList.end(), 0U); | ||
| 236 | + if (totalRankNum != userRankSize) { | ||
| 237 | + HCCL_WARNING( | ||
| 238 | + "[PhysicalLevel][Build] netLayer[%u] inst size sum[%u] mismatches userRankSize[%u]", layer, | ||
| 239 | + totalRankNum, userRankSize); | ||
| 240 | + return HCCL_E_INTERNAL; | ||
| 241 | + } | ||
| 242 | + // 布局自检: 定位到本rank所在的块, 其大小必须等于本地实例的rank数。两个量来源独立, | ||
| 243 | + // 对得上才说明"按最小rankId升序"这个布局假设在本层成立 | ||
| 244 | + u32 cumulative = 0; | ||
| 245 | + for (u32 instSize : instSizeList) { | ||
| 246 | + cumulative += instSize; | ||
| 247 | + if (myRank >= cumulative) { | ||
| 248 | + continue; | ||
| 249 | + } | ||
| 250 | + if (instSize == static_cast<u32>(localRankNum)) { | ||
| 251 | + return HCCL_SUCCESS; | ||
| 252 | + } | ||
| 253 | + HCCL_WARNING( | ||
| 254 | + "[PhysicalLevel][Build] netLayer[%u] rank[%u] locates a block of size[%u] but local " | ||
| 255 | + "instance has [%zu] ranks, inst size list is not laid out by ascending min rankId", | ||
| 256 | + layer, myRank, instSize, localRankNum); | ||
| 257 | + return HCCL_E_INTERNAL; | ||
| 258 | + } | ||
| 259 | + return HCCL_E_INTERNAL; | ||
| 260 | + } | ||
| 261 | + | ||
| 262 | + // 取该netLayer本地NetInstance的rank集合与全层分区。这是"合一"里ranktable那一半: | ||
| 263 | + // 只有NetInstance看得到兄弟实例, 因此只有它能给出全局分区 | ||
| 264 | + HcclResult FetchNetInstance( | ||
| 265 | + HcclComm comm, const TopoInfoWithNetLayerDetails* topoInfo, u32 layer, std::vector<u32>& ranks, | ||
| 266 | + std::vector<u32>& instSizeListByLayer) | ||
| 267 | + { | ||
| 268 | + const NetLayerDetails& details = topoInfo->netLayerDetails; | ||
| 269 | + const u32 myRank = topoInfo->userRank; | ||
| 270 | + if (layer >= details.localNetInsSizeOfLayer.size() || layer >= details.instSizeListOfLayer.size()) { | ||
| 271 | + HCCL_WARNING("[PhysicalLevel][Build] netLayer[%u] out of range of netLayerDetails arrays", layer); | ||
| 272 | + return HCCL_E_INTERNAL; | ||
| 273 | + } | ||
| 274 | + HcclResult ret = FetchLocalNetRanks(comm, details, layer, myRank, ranks); | ||
| 275 | + if (ret != HCCL_SUCCESS) { | ||
| 276 | + return ret; | ||
| 277 | + } | ||
| 278 | + // 原样透传HCOMM的返回序, 不重排。该序是"按最小rankId升序的分区布局", | ||
| 279 | + // topo_host.cc的CalcGroupIdx/GetCurrentServerStartRank已在其上做前缀和定位, 必须与之一致 | ||
| 280 | + instSizeListByLayer = details.instSizeListOfLayer[layer]; | ||
| 281 | + return ValidateInstSizeLayout(instSizeListByLayer, layer, myRank, ranks.size(), topoInfo->userRankSize); | ||
| 282 | + } | ||
| 283 | + | ||
| 284 | + // 取该layer上全部TopoInstance的id。空map是合法结果, 返回空列表且不算失败 | ||
| 285 | + HcclResult FetchTopoInstIds(HcclComm comm, u32 layer, std::vector<u32>& instIds) | ||
| 286 | + { | ||
| 287 | + instIds.clear(); | ||
| 288 | + u32* rawInstIds = nullptr; | ||
| 289 | + u32 instNum = 0; | ||
| 290 | + HcclResult ret = HcclRankGraphGetTopoInstsByLayer(comm, layer, &rawInstIds, &instNum); | ||
| 291 | + if (ret != HCCL_SUCCESS) { | ||
| 292 | + HCCL_WARNING("[PhysicalLevel][Build] get topo insts of layer[%u] failed, ret[%d]", layer, ret); | ||
| 293 | + return HCCL_E_INTERNAL; | ||
| 294 | + } | ||
| 295 | + if (instNum == 0) { | ||
| 296 | + // 空map即返回0且不报错。意味着该层没有endpoints与topoType可供建链, | ||
| 297 | + // 调用方据此把hasTopoInst置false | ||
| 298 | + HCCL_DEBUG("[PhysicalLevel][Build] layer[%u] has no topo instance", layer); | ||
| 299 | + return HCCL_SUCCESS; | ||
| 300 | + } | ||
| 301 | + if (rawInstIds == nullptr) { | ||
| 302 | + HCCL_WARNING("[PhysicalLevel][Build] topo insts of layer[%u] is null while num[%u]", layer, instNum); | ||
| 303 | + return HCCL_E_INTERNAL; | ||
| 304 | + } | ||
| 305 | + // 立即复制: 该接口的下一次调用会clear()并重填同一个成员vector | ||
| 306 | + instIds.assign(rawInstIds, rawInstIds + instNum); | ||
| 307 | + return HCCL_SUCCESS; | ||
| 308 | + } | ||
| 309 | + | ||
| 310 | + // 采集单个TopoInstance并追加到out。当前rank不在其中时跳过且不写out, 不算失败 | ||
| 311 | + HcclResult | ||
| 312 | + AppendTopoInstLevel(HcclComm comm, u32 myRank, u32 layer, u32 instId, std::vector<PhysicalLevelInfo>& out) | ||
| 313 | + { | ||
| 314 | + u32* rawRanks = nullptr; | ||
| 315 | + u32 rankNum = 0; | ||
| 316 | + if (HcclRankGraphGetRanksByTopoInst(comm, layer, instId, &rawRanks, &rankNum) != HCCL_SUCCESS | ||
| 317 | + || rawRanks == nullptr) { | ||
| 318 | + HCCL_WARNING("[PhysicalLevel][Build] get ranks by topo inst[%u] of layer[%u] failed", instId, layer); | ||
| 319 | + return HCCL_E_INTERNAL; | ||
| 320 | + } | ||
| 321 | + std::vector<u32> ranks(rawRanks, rawRanks + rankNum); | ||
| 322 | + | ||
| 323 | + // GetTopoInstsByLayer返回的应当只含当前rank所在的topoInstance, 这里再过滤一次兜底 | ||
| 324 | + if (std::find(ranks.begin(), ranks.end(), myRank) == ranks.end()) { | ||
| 325 | + HCCL_DEBUG( | ||
| 326 | + "[PhysicalLevel][Build] skip sibling topo inst[%u] of layer[%u], myRank[%u] not in it", instId, layer, | ||
| 327 | + myRank); | ||
| 328 | + return HCCL_SUCCESS; | ||
| 329 | + } | ||
| 330 | + | ||
| 331 | + // 必须用按topoInst的GetTopoType。按netLayer的GetTopoTypeByLayer查的是NetType, | ||
| 332 | + // A5上Mesh层是TOPO_FILE_DESC描述的, 会返回COMM_TOPO_CUSTOM, TopoTypeOrder定不了序 | ||
| 333 | + CommTopo topoType = CommTopo::COMM_TOPO_RESERVED; | ||
| 334 | + if (HcclRankGraphGetTopoType(comm, layer, instId, &topoType) != HCCL_SUCCESS) { | ||
| 335 | + HCCL_WARNING("[PhysicalLevel][Build] get topo type of inst[%u] layer[%u] failed", instId, layer); | ||
| 336 | + return HCCL_E_INTERNAL; | ||
| 337 | + } | ||
| 338 | + | ||
| 339 | + PhysicalLevelInfo level; | ||
| 340 | + level.localRanks = std::move(ranks); | ||
| 341 | + level.ref.netLayer = layer; | ||
| 342 | + level.ref.topoInstId = instId; | ||
| 343 | + level.hasTopoInst = true; | ||
| 344 | + level.topoType = topoType; | ||
| 345 | + // 以下三项无返回值: 内部失败一律局部降级, 理由见各自声明处 | ||
| 346 | + FetchEndpoints(comm, layer, instId, level.endpoints); | ||
| 347 | + FetchLocAndProtocols(level.endpoints, level.locType, level.protocols); | ||
| 348 | + FetchPortNums(comm, myRank, level.endpoints, level.portNums); | ||
| 349 | + out.push_back(std::move(level)); | ||
| 350 | + return HCCL_SUCCESS; | ||
| 351 | + } | ||
| 352 | + | ||
| 353 | + // 取该netLayer上、含当前rank的每个TopoInstance的rank集合, 并把链路属性填进level。 | ||
| 354 | + // 这是"合一"里topo那一半: 只有TopoInstance带得出形态/位置/协议/端口数 | ||
| 355 | + HcclResult FetchTopoInstances(HcclComm comm, u32 myRank, u32 layer, std::vector<PhysicalLevelInfo>& out) | ||
| 356 | + { | ||
| 357 | + out.clear(); | ||
| 358 | + std::vector<u32> instIds; | ||
| 359 | + HcclResult ret = FetchTopoInstIds(comm, layer, instIds); | ||
| 360 | + if (ret != HCCL_SUCCESS) { | ||
| 361 | + return ret; | ||
| 362 | + } | ||
| 363 | + for (u32 instId : instIds) { | ||
| 364 | + ret = AppendTopoInstLevel(comm, myRank, layer, instId, out); | ||
| 365 | + if (ret != HCCL_SUCCESS) { | ||
| 366 | + return ret; | ||
| 367 | + } | ||
| 368 | + } | ||
| 369 | + return HCCL_SUCCESS; | ||
| 370 | + } | ||
| 371 | + | ||
| 372 | + /** | ||
| 373 | + * 按netLayer把ranktable层级与topo层级合成候选Level。合并规则: | ||
| 374 | + * 同范围的TopoInstance -> 与NetInstance合并成一级(view=GLOBAL); 更小的 -> 独立成级(view=LOCAL); | ||
| 375 | + * 该层没有TopoInstance -> NetInstance独立成级(hasTopoInst=false)。 | ||
| 376 | + */ | ||
| 377 | + HcclResult BuildLayerCandidates( | ||
| 378 | + HcclComm comm, const TopoInfoWithNetLayerDetails* topoInfo, u32 layer, | ||
| 379 | + std::vector<PhysicalLevelInfo>& candidates) | ||
| 380 | + { | ||
| 381 | + const u32 myRank = topoInfo->userRank; | ||
| 382 | + std::vector<u32> netRanks; | ||
| 383 | + std::vector<u32> instSizeListByLayer; | ||
| 384 | + CHK_RET(FetchNetInstance(comm, topoInfo, layer, netRanks, instSizeListByLayer)); | ||
| 385 | + | ||
| 386 | + std::vector<PhysicalLevelInfo> topoLevels; | ||
| 387 | + CHK_RET(FetchTopoInstances(comm, myRank, layer, topoLevels)); | ||
| 388 | + | ||
| 389 | + // 两侧都来自HCOMM且均为升序无重复, 直接比vector即可 | ||
| 390 | + std::vector<u32> sortedNetRanks = netRanks; | ||
| 391 | + std::sort(sortedNetRanks.begin(), sortedNetRanks.end()); | ||
| 392 | + | ||
| 393 | + bool merged = false; | ||
| 394 | + for (auto& level : topoLevels) { | ||
| 395 | + std::vector<u32> sortedTopoRanks = level.localRanks; | ||
| 396 | + std::sort(sortedTopoRanks.begin(), sortedTopoRanks.end()); | ||
| 397 | + if (sortedTopoRanks == sortedNetRanks) { | ||
| 398 | + // 同范围: 把NetInstance的全局分区并进来。分区是该层的全局事实, 同层多个 | ||
| 399 | + // 同范围TopoInstance(如netLayer 0的Mesh与CLOS)各自都持有它 | ||
| 400 | + level.view = PhysicalLevelView::GLOBAL; | ||
| 401 | + level.instSizeListByLayer = instSizeListByLayer; | ||
| 402 | + merged = true; | ||
| 403 | + } else { | ||
| 404 | + // 比NetInstance更细: 看不到兄弟NetInstance, 没有全局分区可言 | ||
| 405 | + level.view = PhysicalLevelView::LOCAL; | ||
| 406 | + level.instSizeListByLayer.clear(); | ||
| 407 | + } | ||
| 408 | + HCCL_DEBUG( | ||
| 409 | + "[PhysicalLevel][Build] layer[%u] inst[%u] rankNum[%zu] view[%u] topoType[%d] locType[%d] " | ||
| 410 | + "protocolNum[%zu] portNumCnt[%zu]", | ||
| 411 | + layer, level.ref.topoInstId, level.localRanks.size(), static_cast<u32>(level.view), | ||
| 412 | + static_cast<s32>(level.topoType), static_cast<s32>(level.locType), level.protocols.size(), | ||
| 413 | + level.portNums.size()); | ||
| 414 | + candidates.push_back(std::move(level)); | ||
| 415 | + } | ||
| 416 | + | ||
| 417 | + if (!merged) { | ||
| 418 | + // 没有同范围的TopoInstance: 分区信息仍有效但拿不到链路属性, 用hasTopoInst=false显式注明 | ||
| 419 | + PhysicalLevelInfo level; | ||
| 420 | + level.localRanks = std::move(netRanks); | ||
| 421 | + level.view = PhysicalLevelView::GLOBAL; | ||
| 422 | + level.instSizeListByLayer = std::move(instSizeListByLayer); | ||
| 423 | + level.ref.netLayer = layer; | ||
| 424 | + level.ref.topoInstId = INVALID_UINT; | ||
| 425 | + level.hasTopoInst = false; | ||
| 426 | + HCCL_DEBUG( | ||
| 427 | + "[PhysicalLevel][Build] layer[%u] has no same-range topo instance, level carries partition only, " | ||
| 428 | + "rankNum[%zu]", | ||
| 429 | + layer, level.localRanks.size()); | ||
| 430 | + candidates.push_back(std::move(level)); | ||
| 431 | + } | ||
| 432 | + return HCCL_SUCCESS; | ||
| 433 | + } | ||
| 434 | + | ||
| 435 | + HcclResult BuildPhysicalLevelCandidates( | ||
| 436 | + HcclComm comm, const TopoInfoWithNetLayerDetails* topoInfo, std::vector<PhysicalLevelInfo>& candidates) | ||
| 437 | + { | ||
| 438 | + candidates.clear(); | ||
| 439 | + if (topoInfo->netLayerDetails.netLayers.empty()) { | ||
| 440 | + HCCL_WARNING("[PhysicalLevel][Build] netLayers is empty, rank[%u]", topoInfo->userRank); | ||
| 441 | + return HCCL_E_INTERNAL; | ||
| 442 | + } | ||
| 443 | + // 只遍历netLayers里的layer: 规避HCOMM对非法layer的抛异常分支, 并保证ref.netLayer都来自 | ||
| 444 | + // GetLayers的实际结果。收集先后无所谓, LevelLess是全序, 不依赖输入顺序 | ||
| 445 | + for (u32 layer : topoInfo->netLayerDetails.netLayers) { | ||
| 446 | + CHK_RET(BuildLayerCandidates(comm, topoInfo, layer, candidates)); | ||
| 447 | + } | ||
| 448 | + return HCCL_SUCCESS; | ||
| 449 | + } | ||
| 450 | + | ||
| 451 | +} // namespace | ||
| 452 | + | ||
| 453 | +HcclResult BuildPhysicalLevels(HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo) | ||
| 454 | +{ | ||
| 455 | + CHK_PTR_NULL(topoInfo); | ||
| 456 | + topoInfo->physicalLevels.clear(); | ||
| 457 | + if (comm == nullptr) { | ||
| 458 | + HCCL_WARNING("[PhysicalLevel][Build] comm is null, physicalLevels stays empty"); | ||
| 459 | + return HCCL_SUCCESS; | ||
| 460 | + } | ||
| 461 | + | ||
| 462 | + // 在临时对象中构建, 全部校验通过后再赋值; 降级或失败时physicalLevels保持为空 | ||
| 463 | + std::vector<PhysicalLevelInfo> candidates; | ||
| 464 | + std::vector<PhysicalLevelInfo> levels; | ||
| 465 | + | ||
| 466 | + HcclResult ret = BuildPhysicalLevelCandidates(comm, topoInfo, candidates); | ||
| 467 | + if (ret == HCCL_SUCCESS) { | ||
| 468 | + ret = NormalizePhysicalLevels(candidates, topoInfo->userRank, topoInfo->userRankSize, levels); | ||
| 469 | + } | ||
| 470 | + if (ret == HCCL_SUCCESS) { | ||
| 471 | + ret = ValidatePhysicalLevels(levels, topoInfo->userRank, topoInfo->userRankSize); | ||
| 472 | + } | ||
| 473 | + if (ret != HCCL_SUCCESS) { | ||
| 474 | + // 任何失败一律降级, 不改变CalcTopoShape的返回值 | ||
| 475 | + HCCL_WARNING( | ||
| 476 | + "[PhysicalLevel][Build] normalize degraded, ret[%d], rank[%u]. physicalLevels stays empty, legacy path " | ||
| 477 | + "unaffected.", | ||
| 478 | + ret, topoInfo->userRank); | ||
| 479 | + return HCCL_SUCCESS; | ||
| 480 | + } | ||
| 481 | + | ||
| 482 | + topoInfo->physicalLevels = std::move(levels); | ||
| 483 | + const size_t levelNum = topoInfo->physicalLevels.size(); | ||
| 484 | + HCCL_RUN_INFO("[PhysicalLevel][Build] rank[%u] built [%zu] physical levels", topoInfo->userRank, levelNum); | ||
| 485 | + // 最终产物逐级各打一行, 打的是真正落进topoInfo的内容(BuildLayerCandidates那条DEBUG打的是候选)。 | ||
| 486 | + // RUN_INFO让默认日志级别下就搜得到, INFO让这几行与同批INFO落在同一条时间线上 | ||
| 487 | + for (size_t idx = 0; idx < levelNum; ++idx) { | ||
| 488 | + const std::string desc = DescribeLevel(topoInfo->physicalLevels[idx], idx, levelNum); | ||
| 489 | + HCCL_RUN_INFO("[PhysicalLevel][Build] rank[%u] %s", topoInfo->userRank, desc.c_str()); | ||
| 490 | + HCCL_INFO("[PhysicalLevel][Build] rank[%u] %s", topoInfo->userRank, desc.c_str()); | ||
| 491 | + } | ||
| 492 | + return HCCL_SUCCESS; | ||
| 493 | +} | ||
| 494 | + | ||
| 495 | +} // namespace ops_hccl | ||
| @@ -0,0 +1,434 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | +namespace ops_hccl { | ||
| 21 | +namespace { | ||
| 22 | + | ||
| 23 | + void SortUnique(std::vector<u32>& ranks) | ||
| 24 | + { | ||
| 25 | + std::sort(ranks.begin(), ranks.end()); | ||
| 26 | + ranks.erase(std::unique(ranks.begin(), ranks.end()), ranks.end()); | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + bool ContainsRank(const std::vector<u32>& sortedRanks, u32 rank) | ||
| 30 | + { | ||
| 31 | + return std::binary_search(sortedRanks.begin(), sortedRanks.end(), rank); | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + /** | ||
| 35 | + * topoType定序, 含义是互联紧密度递减(MESH直连, CLOS经交换)。不能用枚举值代替 —— 枚举里 | ||
| 36 | + * COMM_TOPO_CLOS=0 < COMM_TOPO_1DMESH=1, 正好相反。返回false(预期外类型)时必须整体降级。 | ||
| 37 | + */ | ||
| 38 | + bool TopoTypeOrder(CommTopo type, u32& order) | ||
| 39 | + { | ||
| 40 | + switch (type) { | ||
| 41 | + case CommTopo::COMM_TOPO_1DMESH: | ||
| 42 | + order = 0; | ||
| 43 | + return true; | ||
| 44 | + case CommTopo::COMM_TOPO_CLOS: | ||
| 45 | + order = 1; | ||
| 46 | + return true; | ||
| 47 | + default: | ||
| 48 | + return false; | ||
| 49 | + } | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + // 无TopoInstance的Level没有形态可言, 排在同键位的有形态Level之后。 | ||
| 53 | + // 取值必须与TopoTypeOrder的输出空间不重叠, 否则两类Level会在第三键上打平 | ||
| 54 | + constexpr u32 TOPO_TYPE_ORDER_NO_TOPO_INST = 2; | ||
| 55 | + | ||
| 56 | + u32 LevelTopoOrder(const PhysicalLevelInfo& level) | ||
| 57 | + { | ||
| 58 | + u32 order = TOPO_TYPE_ORDER_NO_TOPO_INST; | ||
| 59 | + if (level.hasTopoInst) { | ||
| 60 | + // 排序前已逐个校验过topoType可定序, 此处必然成功 | ||
| 61 | + (void)TopoTypeOrder(level.topoType, order); | ||
| 62 | + } | ||
| 63 | + return order; | ||
| 64 | + } | ||
| 65 | + | ||
| 66 | + // 排序三键 + 两个确定性兜底键, 各键语义见下方逐段注释 | ||
| 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 | +namespace { | ||
| 197 | + | ||
| 198 | + // 不变量2: 非空、升序严格递增(等价于无重复)、无越界、含当前rank | ||
| 199 | + HcclResult ValidateRankList(const PhysicalLevelInfo& level, size_t idx, u32 userRank, u32 userRankSize) | ||
| 200 | + { | ||
| 201 | + if (level.localRanks.empty() || !IsStrictlyAscending(level.localRanks)) { | ||
| 202 | + HCCL_WARNING("[PhysicalLevel][Validate] level[%zu] rank list is empty or not ascending", idx); | ||
| 203 | + return HCCL_E_NOT_SUPPORT; | ||
| 204 | + } | ||
| 205 | + if (level.localRanks.back() >= userRankSize) { | ||
| 206 | + HCCL_WARNING( | ||
| 207 | + "[PhysicalLevel][Validate] level[%zu] max rank[%u] exceeds userRankSize[%u]", idx, | ||
| 208 | + level.localRanks.back(), userRankSize); | ||
| 209 | + return HCCL_E_NOT_SUPPORT; | ||
| 210 | + } | ||
| 211 | + if (!ContainsRank(level.localRanks, userRank)) { | ||
| 212 | + HCCL_WARNING("[PhysicalLevel][Validate] level[%zu] does not contain myRank[%u]", idx, userRank); | ||
| 213 | + return HCCL_E_NOT_SUPPORT; | ||
| 214 | + } | ||
| 215 | + return HCCL_SUCCESS; | ||
| 216 | + } | ||
| 217 | + | ||
| 218 | + // 不变量3a: view必须是有效枚举值。底层类型是u32, 不白名单则非法值会静默落进else被当成GLOBAL | ||
| 219 | + // 不变量3b: ref.netLayer恒有效。无效值说明构建侧漏填, 消费侧回查时会拿到错误的层 | ||
| 220 | + HcclResult ValidateViewAndRef(const PhysicalLevelInfo& level, size_t idx) | ||
| 221 | + { | ||
| 222 | + if (level.view != PhysicalLevelView::LOCAL && level.view != PhysicalLevelView::GLOBAL) { | ||
| 223 | + HCCL_WARNING( | ||
| 224 | + "[PhysicalLevel][Validate] level[%zu] has invalid view[%u]", idx, static_cast<u32>(level.view)); | ||
| 225 | + return HCCL_E_NOT_SUPPORT; | ||
| 226 | + } | ||
| 227 | + if (level.ref.netLayer == INVALID_UINT) { | ||
| 228 | + HCCL_WARNING("[PhysicalLevel][Validate] level[%zu] has no valid netLayer", idx); | ||
| 229 | + return HCCL_E_NOT_SUPPORT; | ||
| 230 | + } | ||
| 231 | + return HCCL_SUCCESS; | ||
| 232 | + } | ||
| 233 | + | ||
| 234 | + // 不变量3c之有TopoInstance侧: 链路属性必须自洽, 否则消费侧会把无效值当成真实链路事实建模 | ||
| 235 | + HcclResult ValidateTopoInstAttrs(const PhysicalLevelInfo& level, size_t idx) | ||
| 236 | + { | ||
| 237 | + if (level.ref.topoInstId == INVALID_UINT) { | ||
| 238 | + HCCL_WARNING("[PhysicalLevel][Validate] level[%zu] hasTopoInst but topoInstId is invalid", idx); | ||
| 239 | + return HCCL_E_NOT_SUPPORT; | ||
| 240 | + } | ||
| 241 | + // portNums按iface去重, 条数不会超过endpoint数; 超过说明去重逻辑坏了, 总端口数会被算大 | ||
| 242 | + if (level.portNums.size() > level.endpoints.size()) { | ||
| 243 | + HCCL_WARNING( | ||
| 244 | + "[PhysicalLevel][Validate] level[%zu] portNum count[%zu] exceeds endpoint count[%zu]", idx, | ||
| 245 | + level.portNums.size(), level.endpoints.size()); | ||
| 246 | + return HCCL_E_NOT_SUPPORT; | ||
| 247 | + } | ||
| 248 | + // 0能完整穿过下面的降序检查(排在末尾), 于是不存在的链路会被当成真实出口计入 | ||
| 249 | + for (u32 portNum : level.portNums) { | ||
| 250 | + if (portNum == 0 || portNum > PORT_NUM_SANITY_LIMIT) { | ||
| 251 | + HCCL_WARNING("[PhysicalLevel][Validate] level[%zu] has implausible portNum[%u]", idx, portNum); | ||
| 252 | + return HCCL_E_NOT_SUPPORT; | ||
| 253 | + } | ||
| 254 | + } | ||
| 255 | + // 降序规范化: 采集顺序来自endpoints的哈希序, 不规范化则跨进程字节流不同 | ||
| 256 | + if (!std::is_sorted(level.portNums.begin(), level.portNums.end(), std::greater<u32>())) { | ||
| 257 | + HCCL_WARNING("[PhysicalLevel][Validate] level[%zu] portNums is not sorted descending", idx); | ||
| 258 | + return HCCL_E_NOT_SUPPORT; | ||
| 259 | + } | ||
| 260 | + // protocols去重升序, 理由同上 | ||
| 261 | + if (!std::is_sorted(level.protocols.begin(), level.protocols.end()) | ||
| 262 | + || std::adjacent_find(level.protocols.begin(), level.protocols.end()) != level.protocols.end()) { | ||
| 263 | + HCCL_WARNING("[PhysicalLevel][Validate] level[%zu] protocols is not sorted and unique", idx); | ||
| 264 | + return HCCL_E_NOT_SUPPORT; | ||
| 265 | + } | ||
| 266 | + return HCCL_SUCCESS; | ||
| 267 | + } | ||
| 268 | + | ||
| 269 | + // 不变量3c之无TopoInstance侧: 全部链路属性必须保持无效值。"半有"状态最危险 —— 消费侧判定为 | ||
| 270 | + // 不可用, 却又能从字段里读出看似合理的值 | ||
| 271 | + HcclResult ValidateNoTopoInstAttrs(const PhysicalLevelInfo& level, size_t idx) | ||
| 272 | + { | ||
| 273 | + const bool clean = level.ref.topoInstId == INVALID_UINT && level.topoType == CommTopo::COMM_TOPO_RESERVED | ||
| 274 | + && level.locType == EndpointLocType::ENDPOINT_LOC_TYPE_RESERVED && level.protocols.empty() | ||
| 275 | + && level.portNums.empty() && level.endpoints.empty(); | ||
| 276 | + if (!clean) { | ||
| 277 | + HCCL_WARNING("[PhysicalLevel][Validate] level[%zu] has no topo instance but carries link attributes", idx); | ||
| 278 | + return HCCL_E_NOT_SUPPORT; | ||
| 279 | + } | ||
| 280 | + return HCCL_SUCCESS; | ||
| 281 | + } | ||
| 282 | + | ||
| 283 | + // 不变量4/4b/5: GLOBAL级的instSizeListByLayer是该netLayer对整个通信域的一次完整划分 | ||
| 284 | + HcclResult ValidatePartitionList(const PhysicalLevelInfo& level, size_t idx, u32 userRankSize) | ||
| 285 | + { | ||
| 286 | + const auto begin = level.instSizeListByLayer.begin(); | ||
| 287 | + const auto end = level.instSizeListByLayer.end(); | ||
| 288 | + const u32 total = std::accumulate(begin, end, 0U); | ||
| 289 | + if (level.instSizeListByLayer.empty() || total != userRankSize) { | ||
| 290 | + HCCL_WARNING( | ||
| 291 | + "[PhysicalLevel][Validate] level[%zu] instSizeListByLayer sum[%u] mismatches userRankSize[%u]", idx, | ||
| 292 | + total, userRankSize); | ||
| 293 | + return HCCL_E_NOT_SUPPORT; | ||
| 294 | + } | ||
| 295 | + // 不变量4b: 每个分区非空。0能完整穿过本块其余检查, 于是幽灵空分区会被当成真实Instance计入 | ||
| 296 | + if (std::find(begin, end, 0U) != end) { | ||
| 297 | + HCCL_WARNING("[PhysicalLevel][Validate] level[%zu] instSizeListByLayer contains a zero entry", idx); | ||
| 298 | + return HCCL_E_NOT_SUPPORT; | ||
| 299 | + } | ||
| 300 | + // 不变量5: 当前rank的块大小必须是这一层的某个真实分区。逻辑上被不变量6蕴含, 单列是为了 | ||
| 301 | + // 把两类失败分开: 本条不过是"大小根本不存在", 只有本条过、6不过才是布局假设出了问题 | ||
| 302 | + if (std::find(begin, end, static_cast<u32>(level.localRanks.size())) == end) { | ||
| 303 | + HCCL_WARNING( | ||
| 304 | + "[PhysicalLevel][Validate] level[%zu] localRankNum[%zu] is not one of the inst sizes", idx, | ||
| 305 | + level.localRanks.size()); | ||
| 306 | + return HCCL_E_NOT_SUPPORT; | ||
| 307 | + } | ||
| 308 | + return HCCL_SUCCESS; | ||
| 309 | + } | ||
| 310 | + | ||
| 311 | + // 不变量6: 布局自检。用userRank做前缀和必然落进某一块, 其大小必须等于localRanks.size()。 | ||
| 312 | + // 这是instSizeListByLayer唯一一处能在本地验证的跨rank性质 | ||
| 313 | + HcclResult ValidateRankLocatable(const PhysicalLevelInfo& level, size_t idx, u32 userRank) | ||
| 314 | + { | ||
| 315 | + u32 cumulative = 0; | ||
| 316 | + bool located = false; | ||
| 317 | + for (u32 instSize : level.instSizeListByLayer) { | ||
| 318 | + cumulative += instSize; | ||
| 319 | + if (userRank < cumulative) { | ||
| 320 | + located = (instSize == static_cast<u32>(level.localRanks.size())); | ||
| 321 | + break; | ||
| 322 | + } | ||
| 323 | + } | ||
| 324 | + if (!located) { | ||
| 325 | + HCCL_WARNING( | ||
| 326 | + "[PhysicalLevel][Validate] level[%zu] rank[%u] cannot be located in instSizeListByLayer " | ||
| 327 | + "with a block of localRankNum[%zu]", | ||
| 328 | + idx, userRank, level.localRanks.size()); | ||
| 329 | + return HCCL_E_NOT_SUPPORT; | ||
| 330 | + } | ||
| 331 | + return HCCL_SUCCESS; | ||
| 332 | + } | ||
| 333 | + | ||
| 334 | + // 不变量3d: view与instSizeListByLayer是否为空严格等价; 错开之后消费侧会把只知道本块的级 | ||
| 335 | + // 当成全局分区来切算法 | ||
| 336 | + HcclResult ValidatePartition(const PhysicalLevelInfo& level, size_t idx, u32 userRank, u32 userRankSize) | ||
| 337 | + { | ||
| 338 | + if (level.view == PhysicalLevelView::LOCAL) { | ||
| 339 | + if (!level.instSizeListByLayer.empty()) { | ||
| 340 | + HCCL_WARNING("[PhysicalLevel][Validate] level[%zu] is LOCAL but carries partition sizes", idx); | ||
| 341 | + return HCCL_E_NOT_SUPPORT; | ||
| 342 | + } | ||
| 343 | + return HCCL_SUCCESS; | ||
| 344 | + } | ||
| 345 | + HcclResult ret = ValidatePartitionList(level, idx, userRankSize); | ||
| 346 | + if (ret == HCCL_SUCCESS) { | ||
| 347 | + ret = ValidateRankLocatable(level, idx, userRank); | ||
| 348 | + } | ||
| 349 | + return ret; | ||
| 350 | + } | ||
| 351 | + | ||
| 352 | + HcclResult ValidateLevel(const PhysicalLevelInfo& level, size_t idx, u32 userRank, u32 userRankSize) | ||
| 353 | + { | ||
| 354 | + HcclResult ret = ValidateRankList(level, idx, userRank, userRankSize); | ||
| 355 | + if (ret == HCCL_SUCCESS) { | ||
| 356 | + ret = ValidateViewAndRef(level, idx); | ||
| 357 | + } | ||
| 358 | + if (ret == HCCL_SUCCESS) { | ||
| 359 | + ret = level.hasTopoInst ? ValidateTopoInstAttrs(level, idx) : ValidateNoTopoInstAttrs(level, idx); | ||
| 360 | + } | ||
| 361 | + if (ret == HCCL_SUCCESS) { | ||
| 362 | + ret = ValidatePartition(level, idx, userRank, userRankSize); | ||
| 363 | + } | ||
| 364 | + return ret; | ||
| 365 | + } | ||
| 366 | + | ||
| 367 | + // 不变量7: 大小非递减 + 包含链。允许相等 —— netLayer 0上同范围的MESH与CLOS两级、 | ||
| 368 | + // 两个netLayer的本地NetInstance恰好同范围, 都是合法的相等相邻对 | ||
| 369 | + HcclResult ValidateChain(const std::vector<PhysicalLevelInfo>& levels) | ||
| 370 | + { | ||
| 371 | + for (size_t idx = 1; idx < levels.size(); idx++) { | ||
| 372 | + if (levels[idx].localRanks.size() < levels[idx - 1].localRanks.size()) { | ||
| 373 | + HCCL_WARNING( | ||
| 374 | + "[PhysicalLevel][Validate] level[%zu] rankNum[%zu] is less than level[%zu] rankNum[%zu]", idx, | ||
| 375 | + levels[idx].localRanks.size(), idx - 1, levels[idx - 1].localRanks.size()); | ||
| 376 | + return HCCL_E_NOT_SUPPORT; | ||
| 377 | + } | ||
| 378 | + if (!IsSuperSetOf(levels[idx].localRanks, levels[idx - 1].localRanks)) { | ||
| 379 | + HCCL_WARNING("[PhysicalLevel][Validate] level[%zu] does not contain level[%zu]", idx, idx - 1); | ||
| 380 | + return HCCL_E_NOT_SUPPORT; | ||
| 381 | + } | ||
| 382 | + } | ||
| 383 | + return HCCL_SUCCESS; | ||
| 384 | + } | ||
| 385 | + | ||
| 386 | + // 不变量8: 身份(netLayer, topoInstId)全域唯一。LevelLess的兜底键正是靠这两项才构成全序, | ||
| 387 | + // 重复则两个Level在比较器下等价, std::sort的相对顺序未指定, 各rank的下标语义会分叉。 | ||
| 388 | + // levels规模是个位数(上限PHYSICAL_LEVEL_NUM_LIMIT), 两两比较不需要额外容器 | ||
| 389 | + HcclResult ValidateUniqueSource(const std::vector<PhysicalLevelInfo>& levels) | ||
| 390 | + { | ||
| 391 | + for (size_t i = 0; i < levels.size(); i++) { | ||
| 392 | + for (size_t j = i + 1; j < levels.size(); j++) { | ||
| 393 | + if (levels[i].ref.netLayer == levels[j].ref.netLayer | ||
| 394 | + && levels[i].ref.topoInstId == levels[j].ref.topoInstId) { | ||
| 395 | + HCCL_WARNING( | ||
| 396 | + "[PhysicalLevel][Validate] level[%zu] and level[%zu] share the same source: layer[%u] inst[%u]", | ||
| 397 | + i, j, levels[i].ref.netLayer, levels[i].ref.topoInstId); | ||
| 398 | + return HCCL_E_NOT_SUPPORT; | ||
| 399 | + } | ||
| 400 | + } | ||
| 401 | + } | ||
| 402 | + return HCCL_SUCCESS; | ||
| 403 | + } | ||
| 404 | + | ||
| 405 | +} // namespace | ||
| 406 | + | ||
| 407 | +HcclResult ValidatePhysicalLevels(const std::vector<PhysicalLevelInfo>& levels, u32 userRank, u32 userRankSize) | ||
| 408 | +{ | ||
| 409 | + // 不变量1 | ||
| 410 | + if (userRankSize == 0 || userRank >= userRankSize) { | ||
| 411 | + HCCL_WARNING( | ||
| 412 | + "[PhysicalLevel][Validate] invalid rank info, userRank[%u], userRankSize[%u]", userRank, userRankSize); | ||
| 413 | + return HCCL_E_NOT_SUPPORT; | ||
| 414 | + } | ||
| 415 | + if (levels.empty()) { | ||
| 416 | + HCCL_WARNING("[PhysicalLevel][Validate] levels is empty, rank[%u]", userRank); | ||
| 417 | + return HCCL_E_NOT_SUPPORT; | ||
| 418 | + } | ||
| 419 | + | ||
| 420 | + for (size_t idx = 0; idx < levels.size(); idx++) { | ||
| 421 | + HcclResult ret = ValidateLevel(levels[idx], idx, userRank, userRankSize); | ||
| 422 | + if (ret != HCCL_SUCCESS) { | ||
| 423 | + return ret; | ||
| 424 | + } | ||
| 425 | + } | ||
| 426 | + | ||
| 427 | + HcclResult ret = ValidateChain(levels); | ||
| 428 | + if (ret == HCCL_SUCCESS) { | ||
| 429 | + ret = ValidateUniqueSource(levels); | ||
| 430 | + } | ||
| 431 | + return ret; | ||
| 432 | +} | ||
| 433 | + | ||
| 434 | +} // namespace ops_hccl | ||
| @@ -23,6 +23,7 @@ | |||
| 23 | 23 | ||
| 24 | 24 | ||
| 25 | 25 | ||
| 26 | + | ||
| 26 | 27 | ||
| 27 | constexpr u32 FACTOR_NUM_TWO = 2; | 28 | constexpr u32 FACTOR_NUM_TWO = 2; |
| 28 | constexpr s32 DEVICE_PER_MODULE = 8; | 29 | constexpr s32 DEVICE_PER_MODULE = 8; |
| @@ -784,6 +785,62 @@ static HcclResult CalcLevel2UbRtp(const HcclComm comm, TopoInfoWithNetLayerDetai | |||
| 784 | return HCCL_SUCCESS; | 785 | return HCCL_SUCCESS; |
| 785 | } | 786 | } |
| 786 | 787 | ||
| 788 | +/** | ||
| 789 | + * 查询本卡是否为POD机型。恒返回HCCL_SUCCESS: 该字段是纯附加信息, 取不到时停在false, | ||
| 790 | + * 现有字段与旧执行路径完全不受影响。 | ||
| 791 | + */ | ||
| 792 | +HcclResult CalcDeviceFormFactor(TopoInfoWithNetLayerDetails* topoInfo) | ||
| 793 | +{ | ||
| 794 | + CHK_PTR_NULL(topoInfo); | ||
| 795 | + topoInfo->isPod = false; | ||
| 796 | + | ||
| 797 | + | ||
| 798 | + // 老CANN的acl_rt.h没有ACL_DEV_ATTR_DEVICE_FORM_FACTOR, 无从查起, 一律按非POD建模。 | ||
| 799 | + // 与成功路径同为INFO同前缀: 现场grep一次就能分清走的是哪一条 | ||
| 800 | + HCCL_INFO("[Topo][CalcDeviceFormFactor] acl has no device form factor attr, isPod stays false"); | ||
| 801 | + | ||
| 802 | + // aclrtGetDevice返回的是userDevId, 不是logicDevId, 变量名必须如实反映 | ||
| 803 | + s32 userDevId = 0; | ||
| 804 | + aclError aclRet = aclrtGetDevice(&userDevId); | ||
| 805 | + if (aclRet != ACL_SUCCESS) { | ||
| 806 | + HCCL_WARNING("[Topo][CalcDeviceFormFactor] get current device failed, ret[%d]. isPod stays false.", aclRet); | ||
| 807 | + return HCCL_SUCCESS; | ||
| 808 | + } | ||
| 809 | + | ||
| 810 | + // userDevId -> logicDevId。aclrtGetDeviceInfo要的是logicDevId, 少这一步在配了 | ||
| 811 | + // ASCEND_RT_VISIBLE_DEVICES的环境上会静默读到另一张卡的形态 | ||
| 812 | + s32 logicDevId = 0; | ||
| 813 | + aclRet = aclrtGetLogicDevIdByUserDevId(userDevId, &logicDevId); | ||
| 814 | + if (aclRet != ACL_SUCCESS) { | ||
| 815 | + HCCL_WARNING( | ||
| 816 | + "[Topo][CalcDeviceFormFactor] get logic dev id by user dev id[%d] failed, ret[%d]. " | ||
| 817 | + "isPod stays false.", | ||
| 818 | + userDevId, aclRet); | ||
| 819 | + return HCCL_SUCCESS; | ||
| 820 | + } | ||
| 821 | + | ||
| 822 | + s64 val = 0; | ||
| 823 | + // quiet: 老驱动不支持该infoType时会稳定失败, 按ERROR打会在正常的老环境上持续刷错误日志 | ||
| 824 | + HcclResult ret = hcalrtGetDeviceInfo(static_cast<u32>(logicDevId), ACL_DEV_ATTR_DEVICE_FORM_FACTOR, val, true); | ||
| 825 | + if (ret != HCCL_SUCCESS) { | ||
| 826 | + HCCL_WARNING( | ||
| 827 | + "[Topo][CalcDeviceFormFactor] get device form factor failed, ret[%d], logicDevId[%d]. " | ||
| 828 | + "isPod stays false.", | ||
| 829 | + ret, logicDevId); | ||
| 830 | + return HCCL_SUCCESS; | ||
| 831 | + } | ||
| 832 | + | ||
| 833 | + // 必须是严格相等的正向判断: ACL将来新增形态时, 未识别的取值必须落到false一侧 | ||
| 834 | + topoInfo->isPod = (val == ACL_DEVICE_FORM_FACTOR_POD); | ||
| 835 | + // 原始取值与两个设备号都打出来: 现场据此分辨"确实不是POD"还是"取到了个没见过的形态" | ||
| 836 | + HCCL_INFO( | ||
| 837 | + "[Topo][CalcDeviceFormFactor] userDevId[%d] logicDevId[%d] formFactor[%ld] isPod[%d]", userDevId, logicDevId, | ||
| 838 | + val, static_cast<s32>(topoInfo->isPod)); | ||
| 839 | + | ||
| 840 | + | ||
| 841 | + return HCCL_SUCCESS; | ||
| 842 | +} | ||
| 843 | + | ||
| 787 | HcclResult CalcTopoShape(HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo) | 844 | HcclResult CalcTopoShape(HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo) |
| 788 | { | 845 | { |
| 789 | CHK_RET(ExtractNetLayerDetails(comm, topoInfo)); | 846 | CHK_RET(ExtractNetLayerDetails(comm, topoInfo)); |
| @@ -796,6 +853,11 @@ HcclResult CalcTopoShape(HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo) | |||
| 796 | CHK_RET(CalcTopLevelUboe(comm, topoInfo)); | 853 | CHK_RET(CalcTopLevelUboe(comm, topoInfo)); |
| 797 | CHK_RET(CalcLevel2UbRtp(comm, topoInfo)); | 854 | CHK_RET(CalcLevel2UbRtp(comm, topoInfo)); |
| 798 | CHK_RET(CalcHostDPUOnly(comm, topoInfo)); | 855 | CHK_RET(CalcHostDPUOnly(comm, topoInfo)); |
| 856 | + // 与comm无关, 只查本卡; 恒返回HCCL_SUCCESS, 取不到时停在false | ||
| 857 | + CHK_RET(CalcDeviceFormFactor(topoInfo)); | ||
| 858 | + // 放在最后: 现有字段的提取与派生逻辑完全不受影响, 且可复用已提取的netLayerDetails。 | ||
| 859 | + // BuildPhysicalLevels恒返回HCCL_SUCCESS, 内部失败一律降级为空视图 | ||
| 860 | + CHK_RET(BuildPhysicalLevels(comm, topoInfo)); | ||
| 799 | return HCCL_SUCCESS; | 861 | return HCCL_SUCCESS; |
| 800 | } | 862 | } |
| 801 | 863 | ||
| @@ -94,6 +94,12 @@ HcclResult CalcTopoShape(HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo); | |||
| 94 | 94 | ||
| 95 | HcclResult CalcHostDPUOnly(HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo); | 95 | HcclResult CalcHostDPUOnly(HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo); |
| 96 | 96 | ||
| 97 | +/** | ||
| 98 | + * 查询本卡是否为POD机型并写入topoInfo->isPod。不依赖HcclComm, 只查本设备。 | ||
| 99 | + * 恒返回HCCL_SUCCESS: 取不到时停在false, 不影响任何现有字段与执行路径。 | ||
| 100 | + */ | ||
| 101 | +HcclResult CalcDeviceFormFactor(TopoInfoWithNetLayerDetails* topoInfo); | ||
| 102 | + | ||
| 97 | HcclResult ExtractNetLayerDetails(const HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo); | 103 | HcclResult ExtractNetLayerDetails(const HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo); |
| 98 | 104 | ||
| 99 | HcclResult ExtractTopoDetails(HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo); | 105 | HcclResult ExtractTopoDetails(HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo); |
| @@ -0,0 +1,343 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2025 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | +namespace ops_hccl { | ||
| 15 | + | ||
| 16 | +TopoMatchBaseV2::TopoMatchBaseV2() {} | ||
| 17 | +TopoMatchBaseV2::~TopoMatchBaseV2() {} | ||
| 18 | + | ||
| 19 | +u32 CalcGcdByPair(u32 a, u32 b) | ||
| 20 | +{ | ||
| 21 | + if (a == 0 || b == 0) { | ||
| 22 | + return 1; | ||
| 23 | + } | ||
| 24 | + while (b != 0) { | ||
| 25 | + u32 r = a % b; | ||
| 26 | + a = b; | ||
| 27 | + b = r; | ||
| 28 | + } | ||
| 29 | + HCCL_DEBUG("[CalcGcdByPair] a[%u] b[%u], gcd[%u]", a, b, a); | ||
| 30 | + return a; | ||
| 31 | +} | ||
| 32 | + | ||
| 33 | +u32 CalcGcd(const std::vector<u32>& nums) | ||
| 34 | +{ | ||
| 35 | + if (nums.empty()) { | ||
| 36 | + return 1; | ||
| 37 | + } | ||
| 38 | + u32 result = nums[0]; | ||
| 39 | + for (size_t i = 1; i < nums.size(); i++) { | ||
| 40 | + result = CalcGcdByPair(result, nums[i]); | ||
| 41 | + if (result == 1) { | ||
| 42 | + return 1; | ||
| 43 | + } | ||
| 44 | + } | ||
| 45 | + HCCL_DEBUG("[CalcGcd] size[%u], gcd[%u]", static_cast<u32>(nums.size()), result); | ||
| 46 | + return result; | ||
| 47 | +} | ||
| 48 | + | ||
| 49 | +int32_t FindHighestEffectiveLevel(const std::vector<PhysicalLevelInfo>& physicalLevels) | ||
| 50 | +{ | ||
| 51 | + for (int32_t i = static_cast<int32_t>(physicalLevels.size()) - 1; i >= 0; i--) { | ||
| 52 | + if (physicalLevels[i].hasTopoInst) { | ||
| 53 | + return i; | ||
| 54 | + } | ||
| 55 | + } | ||
| 56 | + return INVALID_PHYSICAL_LEVEL_IDX; | ||
| 57 | +} | ||
| 58 | + | ||
| 59 | +bool IsInstListSymmetric(const std::vector<uint32_t>& instList) | ||
| 60 | +{ | ||
| 61 | + if (instList.empty()) { | ||
| 62 | + HCCL_WARNING("[TopoMatchBase] instList is empty!"); | ||
| 63 | + return true; | ||
| 64 | + } | ||
| 65 | + for (size_t i = 0; i < instList.size(); i++) { | ||
| 66 | + if (instList[i] != instList[0]) { | ||
| 67 | + return false; | ||
| 68 | + } | ||
| 69 | + } | ||
| 70 | + return true; | ||
| 71 | +} | ||
| 72 | + | ||
| 73 | +std::vector<u32> BuildRepresentativeGroup(u32 step, u32 count, u32 offset) | ||
| 74 | +{ | ||
| 75 | + std::vector<u32> group; | ||
| 76 | + group.reserve(count); | ||
| 77 | + for (u32 i = 0; i < count; i++) { | ||
| 78 | + group.push_back(offset + i * step); | ||
| 79 | + } | ||
| 80 | + return group; | ||
| 81 | +} | ||
| 82 | + | ||
| 83 | +HcclResult ValidateGroup(const std::vector<u32>& group, u32 dim, u32 myRank, const std::string& levelName) | ||
| 84 | +{ | ||
| 85 | + if (group.size() != dim) { | ||
| 86 | + HCCL_ERROR( | ||
| 87 | + "[TopoMatchBase] Rank [%u], %s group size[%zu] != dim[%u].", myRank, levelName.c_str(), 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 | + | ||
| 101 | + for (CommProtocol p : protocols) { | ||
| 102 | + if (p == COMM_PROTOCOL_UBG) { | ||
| 103 | + return true; | ||
| 104 | + } | ||
| 105 | + } | ||
| 106 | + | ||
| 107 | + return false; | ||
| 108 | +} | ||
| 109 | + | ||
| 110 | +// 按引擎过滤收集 hasTopoInst 的物理层序号:非 hostdpu 排除 HOST 层,AIV 排除含 UBG 链路的层 | ||
| 111 | +std::vector<u32> CollectEffectiveIndices(const std::vector<PhysicalLevelInfo>& physicalLevels, OpExecuteConfig engine) | ||
| 112 | +{ | ||
| 113 | + bool isHostdpu = (engine == OpExecuteConfig::HOSTCPU); | ||
| 114 | + bool isAiv = (engine == OpExecuteConfig::AIV); | ||
| 115 | + std::vector<u32> effIdx; | ||
| 116 | + for (u32 i = 0; i < physicalLevels.size(); i++) { | ||
| 117 | + if (!physicalLevels[i].hasTopoInst) { | ||
| 118 | + HCCL_INFO("[CollectEffectiveIndices] skip level[%u]: no TopoInstance.", i); | ||
| 119 | + continue; | ||
| 120 | + } | ||
| 121 | + if (!isHostdpu && physicalLevels[i].locType == EndpointLocType::ENDPOINT_LOC_TYPE_HOST) { | ||
| 122 | + HCCL_INFO("[CollectEffectiveIndices] skip level[%u]: HOST locType (non-hostdpu excludes HOST).", i); | ||
| 123 | + continue; | ||
| 124 | + } | ||
| 125 | + if (isAiv && HasUbgLink(physicalLevels[i].protocols)) { | ||
| 126 | + HCCL_INFO("[CollectEffectiveIndices] skip level[%u]: UBG protocol (AIV excludes UBG).", i); | ||
| 127 | + continue; | ||
| 128 | + } | ||
| 129 | + effIdx.push_back(i); | ||
| 130 | + } | ||
| 131 | + return effIdx; | ||
| 132 | +} | ||
| 133 | + | ||
| 134 | +// 判断算法是否属于 Mesh 类 | ||
| 135 | +bool IsMeshAlgo(AlgoType algo) { return MESH_ALGO_TYPES.count(algo) > 0; } | ||
| 136 | + | ||
| 137 | +// 判断算法是否属于 MeshConcur 类(触发 CLOS 双层规则) | ||
| 138 | +bool IsMeshConcurAlgo(AlgoType algo) { return MESH_CONCUR_ALGO_TYPES.count(algo) > 0; } | ||
| 139 | + | ||
| 140 | +// 段内匹配:算法 [algoLow..algoHigh] ↔ 物理 [physLow..physHigh],低层一一 + 最高层压缩多余 | ||
| 141 | +static void MatchLayerIdxBySegment(u32 algoLow, u32 algoHigh, u32 physLow, u32 physHigh, std::vector<u32>& pIndices) | ||
| 142 | +{ | ||
| 143 | + if (algoLow > algoHigh) { | ||
| 144 | + return; | ||
| 145 | + } | ||
| 146 | + u32 algoCount = algoHigh - algoLow + 1; | ||
| 147 | + for (u32 k = 0; k + 1 < algoCount; k++) { | ||
| 148 | + pIndices[algoLow + k] = physLow + k; | ||
| 149 | + } | ||
| 150 | + pIndices[algoHigh] = physHigh; | ||
| 151 | +} | ||
| 152 | + | ||
| 153 | +// hostdpu 强约束:最高算法层锚定 HOST 且 localRanks==userRankSize 的物理层(从高到低找首个), | ||
| 154 | +// 并校验 HOST 锚点以下物理层数 >= 剩余待匹配算法层数;找不到或不满足则 not support | ||
| 155 | +static HcclResult AnchorHostDpu( | ||
| 156 | + const std::vector<PhysicalLevelInfo>& physicalLevels, const std::vector<u32>& effIdx, u32 userRankSize, u32 topAlgo, | ||
| 157 | + u32& topPhysPos, std::set<u32>& anchoredPhys, std::map<u32, u32>& anchors) | ||
| 158 | +{ | ||
| 159 | + bool found = false; | ||
| 160 | + for (int32_t k = static_cast<int32_t>(effIdx.size()) - 1; k >= 0; k--) { | ||
| 161 | + const PhysicalLevelInfo& lvl = physicalLevels[effIdx[k]]; | ||
| 162 | + if (lvl.locType == EndpointLocType::ENDPOINT_LOC_TYPE_HOST && lvl.localRanks.size() == userRankSize) { | ||
| 163 | + anchors[topAlgo] = static_cast<u32>(k); | ||
| 164 | + anchoredPhys.insert(static_cast<u32>(k)); | ||
| 165 | + topPhysPos = static_cast<u32>(k); | ||
| 166 | + HCCL_INFO( | ||
| 167 | + "[FindAnchors] hostdpu: algo level[%u] anchored to phys[%u] (HOST, localRankSize==%u).", topAlgo, | ||
| 168 | + effIdx[k], userRankSize); | ||
| 169 | + found = true; | ||
| 170 | + break; | ||
| 171 | + } | ||
| 172 | + } | ||
| 173 | + if (!found) { | ||
| 174 | + HCCL_INFO( | ||
| 175 | + "[FindAnchors] hostdpu but no HOST layer with localRanks==userRankSize[%u], not support.", userRankSize); | ||
| 176 | + return HcclResult::HCCL_E_NOT_SUPPORT; | ||
| 177 | + } | ||
| 178 | + // HOST 锚点以下的物理层数(= topPhysPos)须 >= 剩余待匹配的算法层数(= topAlgo),否则低层无足够物理层 | ||
| 179 | + if (topPhysPos < topAlgo) { | ||
| 180 | + HCCL_INFO( | ||
| 181 | + "[FindAnchors] hostdpu phys layers below host[%u] < remaining algo levels[%u], not support.", topPhysPos, | ||
| 182 | + topAlgo); | ||
| 183 | + return HcclResult::HCCL_E_NOT_SUPPORT; | ||
| 184 | + } | ||
| 185 | + return HcclResult::HCCL_SUCCESS; | ||
| 186 | +} | ||
| 187 | + | ||
| 188 | +// Mesh 锚点:算法层从低到高遍历,优先匹配 COMM_TOPO_1DMESH 物理层,不可重复锚定; | ||
| 189 | +// hostdpu 已锚定的最高层跳过;MeshConcur 未匹配到则 not support | ||
| 190 | +static HcclResult AnchorMeshLevels( | ||
| 191 | + const std::vector<PhysicalLevelInfo>& physicalLevels, const std::vector<u32>& effIdx, | ||
| 192 | + const std::vector<AlgoType>& algoTypes, u32 topAlgo, u32 topPhysPos, std::set<u32>& anchoredPhys, | ||
| 193 | + std::map<u32, u32>& anchors) | ||
| 194 | +{ | ||
| 195 | + for (u32 i = 0; i < algoTypes.size(); i++) { | ||
| 196 | + if (anchors.count(i) > 0) { | ||
| 197 | + continue; | ||
| 198 | + } | ||
| 199 | + if (!IsMeshAlgo(algoTypes[i])) { | ||
| 200 | + continue; | ||
| 201 | + } | ||
| 202 | + // 为上层算法层(i+1..topAlgo)留足物理位:candidateHigh = topPhysPos - (topAlgo - i) | ||
| 203 | + u32 candidateHigh = topPhysPos - (topAlgo - i); | ||
| 204 | + bool found = false; | ||
| 205 | + for (u32 k = i; k <= candidateHigh; k++) { | ||
| 206 | + if (anchoredPhys.count(k) > 0) { | ||
| 207 | + continue; | ||
| 208 | + } | ||
| 209 | + if (physicalLevels[effIdx[k]].topoType == COMM_TOPO_1DMESH) { | ||
| 210 | + anchors[i] = k; | ||
| 211 | + anchoredPhys.insert(k); | ||
| 212 | + HCCL_INFO("[FindAnchors] mesh: algo level[%u] anchored to phys[%u] (1DMESH).", i, effIdx[k]); | ||
| 213 | + found = true; | ||
| 214 | + break; | ||
| 215 | + } | ||
| 216 | + } | ||
| 217 | + if (!found && IsMeshConcurAlgo(algoTypes[i])) { | ||
| 218 | + HCCL_INFO("[FindAnchors] algo[%u] MeshConcur but no Mesh layer, not support.", i); | ||
| 219 | + return HcclResult::HCCL_E_NOT_SUPPORT; | ||
| 220 | + } | ||
| 221 | + } | ||
| 222 | + return HcclResult::HCCL_SUCCESS; | ||
| 223 | +} | ||
| 224 | + | ||
| 225 | +// 锚点匹配:hostdpu 强约束最高层选 HOST(优先级高于 Mesh)+ Mesh 层优先匹配 COMM_TOPO_1DMESH | ||
| 226 | +HcclResult FindAnchors( | ||
| 227 | + const std::vector<PhysicalLevelInfo>& physicalLevels, const std::vector<u32>& effIdx, | ||
| 228 | + const std::vector<AlgoType>& algoTypes, OpExecuteConfig engine, u32 userRankSize, std::map<u32, u32>& anchors) | ||
| 229 | +{ | ||
| 230 | + std::set<u32> anchoredPhys; | ||
| 231 | + u32 topAlgo = static_cast<u32>(algoTypes.size()) - 1; | ||
| 232 | + // 最高算法层对应的物理 effIdx 位置:非 hostdpu 由尾段取 effIdx.back();hostdpu 取 HOST 锚点 | ||
| 233 | + u32 topPhysPos = static_cast<u32>(effIdx.size()) - 1; | ||
| 234 | + if (engine == OpExecuteConfig::HOSTCPU) { | ||
| 235 | + CHK_RET(AnchorHostDpu(physicalLevels, effIdx, userRankSize, topAlgo, topPhysPos, anchoredPhys, anchors)); | ||
| 236 | + } | ||
| 237 | + CHK_RET(AnchorMeshLevels(physicalLevels, effIdx, algoTypes, topAlgo, topPhysPos, anchoredPhys, anchors)); | ||
| 238 | + return HcclResult::HCCL_SUCCESS; | ||
| 239 | +} | ||
| 240 | + | ||
| 241 | +// 分段压缩:按锚点将算法层与物理层分段,每段低层一一 + 最高层压缩多余物理层 | ||
| 242 | +HcclResult ResolveSegmentMapping( | ||
| 243 | + const std::vector<u32>& effIdx, const std::vector<AlgoType>& algoTypes, const std::map<u32, u32>& anchors, | ||
| 244 | + std::vector<u32>& pIndices) | ||
| 245 | +{ | ||
| 246 | + pIndices.resize(algoTypes.size(), INVALID_UINT); | ||
| 247 | + u32 algoStart = 0; | ||
| 248 | + u32 physStart = 0; | ||
| 249 | + for (const auto& [anchorAlgo, anchorPhys] : anchors) { | ||
| 250 | + // 前段存在当且仅当锚点之前同时有算法层与物理层;anchorAlgo==algoStart 时 anchorAlgo-1 会 u32 下溢,须跳过 | ||
| 251 | + if (anchorAlgo > algoStart && anchorPhys > physStart) { | ||
| 252 | + MatchLayerIdxBySegment(algoStart, anchorAlgo - 1, physStart, anchorPhys - 1, pIndices); | ||
| 253 | + } | ||
| 254 | + pIndices[anchorAlgo] = anchorPhys; | ||
| 255 | + algoStart = anchorAlgo + 1; | ||
| 256 | + physStart = anchorPhys + 1; | ||
| 257 | + } | ||
| 258 | + MatchLayerIdxBySegment(algoStart, algoTypes.size() - 1, physStart, effIdx.size() - 1, pIndices); | ||
| 259 | + return HcclResult::HCCL_SUCCESS; | ||
| 260 | +} | ||
| 261 | + | ||
| 262 | +// 在 meshEffPos 之上(更高 index)找首个 localRanks 包含 mesh 层 localRanks 的物理层;找不到返回 | ||
| 263 | +// INVALID_PHYSICAL_LEVEL_IDX | ||
| 264 | +int32_t FindUpperEncompassingLevel( | ||
| 265 | + const std::vector<PhysicalLevelInfo>& physicalLevels, const std::vector<u32>& effIdx, u32 meshEffPos) | ||
| 266 | +{ | ||
| 267 | + const auto& meshRanks = physicalLevels[effIdx[meshEffPos]].localRanks; | ||
| 268 | + for (u32 k = meshEffPos + 1; k < effIdx.size(); k++) { | ||
| 269 | + const auto& upperRanks = physicalLevels[effIdx[k]].localRanks; | ||
| 270 | + if (std::includes(upperRanks.begin(), upperRanks.end(), meshRanks.begin(), meshRanks.end())) { | ||
| 271 | + return static_cast<int32_t>(k); | ||
| 272 | + } | ||
| 273 | + } | ||
| 274 | + return INVALID_PHYSICAL_LEVEL_IDX; | ||
| 275 | +} | ||
| 276 | + | ||
| 277 | +// 引擎过滤 + 锚点匹配 + 分段,得 effIdx 与 pIndices;校验最高层 localRanks==userRankSize | ||
| 278 | +HcclResult ResolveMapping( | ||
| 279 | + const std::vector<PhysicalLevelInfo>& physicalLevels, const AlgAttrs& profile, u32 userRankSize, | ||
| 280 | + std::vector<u32>& effIdx, std::vector<u32>& pIndices) | ||
| 281 | +{ | ||
| 282 | + effIdx = CollectEffectiveIndices(physicalLevels, profile.engine); | ||
| 283 | + u32 algoLevelNum = profile.algoTypes.size(); | ||
| 284 | + if (effIdx.size() < algoLevelNum) { | ||
| 285 | + HCCL_INFO("[ResolveMapping] valid level num[%zu] < algoLevelNum[%u].", effIdx.size(), algoLevelNum); | ||
| 286 | + return HcclResult::HCCL_E_NOT_SUPPORT; | ||
| 287 | + } | ||
| 288 | + std::map<u32, u32> anchors; | ||
| 289 | + // 锚点匹配:含 MeshConcur 的 1DMESH 校验与 hostdpu 强约束,须无条件执行(1:1 时也需校验底层 1DMESH) | ||
| 290 | + CHK_RET(FindAnchors(physicalLevels, effIdx, profile.algoTypes, profile.engine, userRankSize, anchors)); | ||
| 291 | + CHK_RET(ResolveSegmentMapping(effIdx, profile.algoTypes, anchors, pIndices)); | ||
| 292 | + // 最高算法层 localRanks 必须等于 userRankSize | ||
| 293 | + u32 topPhys = effIdx[pIndices[algoLevelNum - 1]]; | ||
| 294 | + if (physicalLevels[topPhys].localRanks.size() != userRankSize) { | ||
| 295 | + HCCL_INFO( | ||
| 296 | + "[ResolveMapping] top layer localRanks[%zu] != userRankSize[%u].", | ||
| 297 | + physicalLevels[topPhys].localRanks.size(), userRankSize); | ||
| 298 | + return HcclResult::HCCL_E_NOT_SUPPORT; | ||
| 299 | + } | ||
| 300 | + return HcclResult::HCCL_SUCCESS; | ||
| 301 | +} | ||
| 302 | + | ||
| 303 | +// 填充 physicalIdxForAlgoLevels(二级):MeshConcur 层记 {Mesh层, 上层超集层},普通层记 {该层} | ||
| 304 | +HcclResult FillPhysicalIdxForAlgoLevels( | ||
| 305 | + const std::vector<PhysicalLevelInfo>& physicalLevels, const std::vector<u32>& effIdx, | ||
| 306 | + const std::vector<u32>& pIndices, const std::vector<AlgoType>& algoTypes, | ||
| 307 | + std::vector<std::vector<PhysicalLevelIndex>>& physicalIdxForAlgoLevels) | ||
| 308 | +{ | ||
| 309 | + physicalIdxForAlgoLevels.resize(algoTypes.size()); | ||
| 310 | + for (u32 i = 0; i < algoTypes.size(); i++) { | ||
| 311 | + u32 physIdx = effIdx[pIndices[i]]; | ||
| 312 | + if (IsMeshConcurAlgo(algoTypes[i])) { | ||
| 313 | + int32_t upperPos = FindUpperEncompassingLevel(physicalLevels, effIdx, pIndices[i]); | ||
| 314 | + if (upperPos == INVALID_PHYSICAL_LEVEL_IDX) { | ||
| 315 | + HCCL_INFO("[FillPhysicalIdx] level[%u] MeshConcur no upper encompassing layer, not support.", i); | ||
| 316 | + return HcclResult::HCCL_E_NOT_SUPPORT; | ||
| 317 | + } | ||
| 318 | + physicalIdxForAlgoLevels[i] | ||
| 319 | + = {static_cast<PhysicalLevelIndex>(physIdx), static_cast<PhysicalLevelIndex>(effIdx[upperPos])}; | ||
| 320 | + } else { | ||
| 321 | + physicalIdxForAlgoLevels[i] = {static_cast<PhysicalLevelIndex>(physIdx)}; | ||
| 322 | + } | ||
| 323 | + } | ||
| 324 | + return HcclResult::HCCL_SUCCESS; | ||
| 325 | +} | ||
| 326 | + | ||
| 327 | +std::string FormatPhysicalIdxForAlgoLevels(const std::vector<std::vector<PhysicalLevelIndex>>& physicalIdxForAlgoLevels) | ||
| 328 | +{ | ||
| 329 | + std::string idxStr; | ||
| 330 | + for (size_t i = 0; i < physicalIdxForAlgoLevels.size(); i++) { | ||
| 331 | + idxStr += "{"; | ||
| 332 | + for (size_t j = 0; j < physicalIdxForAlgoLevels[i].size(); j++) { | ||
| 333 | + idxStr += std::to_string(static_cast<u32>(physicalIdxForAlgoLevels[i][j])); | ||
| 334 | + if (j + 1 < physicalIdxForAlgoLevels[i].size()) { | ||
| 335 | + idxStr += ","; | ||
| 336 | + } | ||
| 337 | + } | ||
| 338 | + idxStr += "} "; | ||
| 339 | + } | ||
| 340 | + return idxStr; | ||
| 341 | +} | ||
| 342 | + | ||
| 343 | +} // namespace ops_hccl | ||
| @@ -0,0 +1,103 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2025 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | +namespace ops_hccl { | ||
| 24 | + | ||
| 25 | +// topo match 相关常量 | ||
| 26 | +constexpr int32_t INVALID_PHYSICAL_LEVEL_IDX = -1; | ||
| 27 | +constexpr u32 ALGO_LEVEL_NUM_TWO = 2; | ||
| 28 | +constexpr u32 ALGO_LEVEL_NUM_THREE = 3; | ||
| 29 | +constexpr u32 CONCURRENT_SUBGROUP_NUM = 2; | ||
| 30 | + | ||
| 31 | +// 辗转相除求两数最大公约数;a 或 b 为 0 时返回 1 避免退化 | ||
| 32 | +u32 CalcGcdByPair(u32 a, u32 b); | ||
| 33 | + | ||
| 34 | +// 对一组数逐对归约求最大公约数,result==1 时早停 | ||
| 35 | +u32 CalcGcd(const std::vector<u32>& nums); | ||
| 36 | + | ||
| 37 | +// 从高到低找首个 hasTopoInst 的物理层序号;不存在返回 INVALID_PHYSICAL_LEVEL_IDX | ||
| 38 | +int32_t FindHighestEffectiveLevel(const std::vector<PhysicalLevelInfo>& physicalLevels); | ||
| 39 | + | ||
| 40 | +// instList 各元素是否全等(对称判定) | ||
| 41 | +bool IsInstListSymmetric(const std::vector<uint32_t>& instList); | ||
| 42 | + | ||
| 43 | +// 构造跨层代表 rank:count 个,从 offset 起、按 step 步长(offset 取 myRank 在本层的偏移,保证 myRank 命中) | ||
| 44 | +std::vector<u32> BuildRepresentativeGroup(u32 step, u32 count, u32 offset); | ||
| 45 | + | ||
| 46 | +// 校验单个 group:规模等于 dim 且包含 myRank;失败打 ERROR 并返回 HCCL_E_INTERNAL | ||
| 47 | +HcclResult ValidateGroup(const std::vector<u32>& group, u32 dim, u32 myRank, const std::string& levelName); | ||
| 48 | + | ||
| 49 | +// 引擎过滤:非 hostdpu 排除 HOST 层,AIV 排除含 UBG 链路的层 | ||
| 50 | +std::vector<u32> CollectEffectiveIndices(const std::vector<PhysicalLevelInfo>& physicalLevels, OpExecuteConfig engine); | ||
| 51 | + | ||
| 52 | +// 判断算法是否属于 Mesh 类 | ||
| 53 | +bool IsMeshAlgo(AlgoType algo); | ||
| 54 | + | ||
| 55 | +// 判断算法是否属于 MeshConcur 类(触发 CLOS 双层规则) | ||
| 56 | +bool IsMeshConcurAlgo(AlgoType algo); | ||
| 57 | + | ||
| 58 | +// 锚点匹配:hostdpu 强约束最高算法层锚定 HOST 且 localRanks==userRankSize 的物理层;Mesh 算法优先匹配 COMM_TOPO_1DMESH | ||
| 59 | +// 物理层(不可重复锚定) | ||
| 60 | +HcclResult FindAnchors( | ||
| 61 | + const std::vector<PhysicalLevelInfo>& physicalLevels, const std::vector<u32>& effIdx, | ||
| 62 | + const std::vector<AlgoType>& algoTypes, OpExecuteConfig engine, u32 userRankSize, std::map<u32, u32>& anchors); | ||
| 63 | + | ||
| 64 | +// 分段压缩得各算法层对应的物理层 effIdx position | ||
| 65 | +HcclResult ResolveSegmentMapping( | ||
| 66 | + const std::vector<u32>& effIdx, const std::vector<AlgoType>& algoTypes, const std::map<u32, u32>& anchors, | ||
| 67 | + std::vector<u32>& pIndices); | ||
| 68 | + | ||
| 69 | +// 引擎过滤 + 锚点匹配 + 分段,得 effIdx 与 pIndices;校验最高层 localRanks==userRankSize | ||
| 70 | +HcclResult ResolveMapping( | ||
| 71 | + const std::vector<PhysicalLevelInfo>& physicalLevels, const AlgAttrs& profile, u32 userRankSize, | ||
| 72 | + std::vector<u32>& effIdx, std::vector<u32>& pIndices); | ||
| 73 | + | ||
| 74 | +// 在 meshEffPos 之上找首个 localRanks 包含 mesh 层 localRanks 的物理层 | ||
| 75 | +int32_t FindUpperEncompassingLevel( | ||
| 76 | + const std::vector<PhysicalLevelInfo>& physicalLevels, const std::vector<u32>& effIdx, u32 meshEffPos); | ||
| 77 | + | ||
| 78 | +// 填充 physicalIdxForAlgoLevels(二级):MeshConcur 层记 {Mesh层, 上层超集层},普通层记 {该层} | ||
| 79 | +HcclResult FillPhysicalIdxForAlgoLevels( | ||
| 80 | + const std::vector<PhysicalLevelInfo>& physicalLevels, const std::vector<u32>& effIdx, | ||
| 81 | + const std::vector<u32>& pIndices, const std::vector<AlgoType>& algoTypes, | ||
| 82 | + std::vector<std::vector<PhysicalLevelIndex>>& physicalIdxForAlgoLevels); | ||
| 83 | + | ||
| 84 | +// 将 physicalIdxForAlgoLevels 拼成日志字符串,每层用 {} 包裹,层内多值逗号分隔,如 "{0,1} {2} {3}" | ||
| 85 | +std::string | ||
| 86 | +FormatPhysicalIdxForAlgoLevels(const std::vector<std::vector<PhysicalLevelIndex>>& physicalIdxForAlgoLevels); | ||
| 87 | + | ||
| 88 | +// V2 基类:MatchTopo 增加 AlgAttrs 参数 | ||
| 89 | +class TopoMatchBaseV2 { | ||
| 90 | +public: | ||
| 91 | + explicit TopoMatchBaseV2(); | ||
| 92 | + virtual ~TopoMatchBaseV2(); | ||
| 93 | + | ||
| 94 | + virtual std::string Describe() const = 0; | ||
| 95 | + | ||
| 96 | + virtual HcclResult MatchTopo( | ||
| 97 | + TopoInfoWithNetLayerDetails* topoInfo, AlgHierarchyInfoForAllLevel& algHierarchyInfo, const AlgAttrs& profile) | ||
| 98 | + = 0; | ||
| 99 | +}; | ||
| 100 | + | ||
| 101 | +} // namespace ops_hccl | ||
| 102 | + | ||
| 103 | + | ||
| @@ -0,0 +1,62 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2025 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | +namespace ops_hccl { | ||
| 16 | + | ||
| 17 | +TopoMatchConcurrentV2::TopoMatchConcurrentV2() {} | ||
| 18 | + | ||
| 19 | +TopoMatchConcurrentV2::~TopoMatchConcurrentV2() {} | ||
| 20 | + | ||
| 21 | +HcclResult TopoMatchConcurrentV2::MatchTopo( | ||
| 22 | + TopoInfoWithNetLayerDetails* topoInfo, AlgHierarchyInfoForAllLevel& algHierarchyInfo, const AlgAttrs& profile) | ||
| 23 | +{ | ||
| 24 | + u32 myRank = topoInfo->userRank; | ||
| 25 | + const auto& physicalLevels = topoInfo->physicalLevels; | ||
| 26 | + if (physicalLevels.empty()) { | ||
| 27 | + HCCL_ERROR("[TopoMatchConcurrentV2] Rank [%u], physicalLevels is empty.", myRank); | ||
| 28 | + return HcclResult::HCCL_E_INTERNAL; | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + // 引擎过滤后收集有效层 | ||
| 32 | + std::vector<u32> effIdx = CollectEffectiveIndices(physicalLevels, profile.engine); | ||
| 33 | + u32 effNum = effIdx.size(); | ||
| 34 | + CHK_PRT_RET( | ||
| 35 | + 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( | ||
| 39 | + (topoInfo->userRankSize == 0), 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( | ||
| 57 | + "[TopoMatchConcurrentV2] Rank [%u], rankSize[%u], physicalIdxForAlgoLevels: [%s].", myRank, | ||
| 58 | + topoInfo->userRankSize, FormatPhysicalIdxForAlgoLevels(algHierarchyInfo.physicalIdxForAlgoLevels).c_str()); | ||
| 59 | + return HcclResult::HCCL_SUCCESS; | ||
| 60 | +} | ||
| 61 | + | ||
| 62 | +} // namespace ops_hccl | ||
| @@ -0,0 +1,35 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2025 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | +namespace ops_hccl { | ||
| 17 | + | ||
| 18 | +class TopoMatchConcurrentV2 : public TopoMatchBaseV2 { | ||
| 19 | +public: | ||
| 20 | + explicit TopoMatchConcurrentV2(); | ||
| 21 | + ~TopoMatchConcurrentV2() override; | ||
| 22 | + | ||
| 23 | + std::string Describe() const override | ||
| 24 | + { | ||
| 25 | + return "Topo Match for Concurrent Algorithm V2 (supports 950/960 out-place devices)."; | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | + HcclResult MatchTopo( | ||
| 29 | + TopoInfoWithNetLayerDetails* topoInfo, AlgHierarchyInfoForAllLevel& algHierarchyInfo, | ||
| 30 | + const AlgAttrs& profile) override; | ||
| 31 | +}; | ||
| 32 | + | ||
| 33 | +} // namespace ops_hccl | ||
| 34 | + | ||
| 35 | + | ||
| @@ -0,0 +1,74 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2025 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | +namespace ops_hccl { | ||
| 16 | + | ||
| 17 | +TopoMatchOneLevel::TopoMatchOneLevel() {} | ||
| 18 | + | ||
| 19 | +TopoMatchOneLevel::~TopoMatchOneLevel() {} | ||
| 20 | + | ||
| 21 | +namespace { | ||
| 22 | + // 从 effIdx 中找 localRanks==userRankSize 的最低有效层;hostdpu 额外要求 locType==HOST | ||
| 23 | + u32 PickFullLocalRanksLayer( | ||
| 24 | + const std::vector<PhysicalLevelInfo>& physicalLevels, const std::vector<u32>& effIdx, u32 userRankSize, | ||
| 25 | + bool requireHost) | ||
| 26 | + { | ||
| 27 | + for (u32 idx : effIdx) { | ||
| 28 | + if (physicalLevels[idx].localRanks.size() != userRankSize) { | ||
| 29 | + continue; | ||
| 30 | + } | ||
| 31 | + if (requireHost && physicalLevels[idx].locType != EndpointLocType::ENDPOINT_LOC_TYPE_HOST) { | ||
| 32 | + continue; | ||
| 33 | + } | ||
| 34 | + return idx; | ||
| 35 | + } | ||
| 36 | + return INVALID_UINT; | ||
| 37 | + } | ||
| 38 | +} // namespace | ||
| 39 | + | ||
| 40 | +HcclResult TopoMatchOneLevel::MatchTopo( | ||
| 41 | + TopoInfoWithNetLayerDetails* topoInfo, AlgHierarchyInfoForAllLevel& algHierarchyInfo, const AlgAttrs& profile) | ||
| 42 | +{ | ||
| 43 | + const auto& physicalLevels = topoInfo->physicalLevels; | ||
| 44 | + if (physicalLevels.empty() || topoInfo->userRankSize == 0) { | ||
| 45 | + HCCL_ERROR("[TopoMatchOneLevel] Rank [%u], physicalLevels empty or userRankSize 0.", topoInfo->userRank); | ||
| 46 | + return HcclResult::HCCL_E_INTERNAL; | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + std::vector<u32> effIdx = CollectEffectiveIndices(physicalLevels, profile.engine); | ||
| 50 | + if (effIdx.empty()) { | ||
| 51 | + HCCL_INFO("[TopoMatchOneLevel] Rank [%u], no valid layer after engine filter.", topoInfo->userRank); | ||
| 52 | + return HcclResult::HCCL_E_NOT_SUPPORT; | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + bool requireHost = (profile.engine == OpExecuteConfig::HOSTCPU); | ||
| 56 | + u32 picked = PickFullLocalRanksLayer(physicalLevels, effIdx, topoInfo->userRankSize, requireHost); | ||
| 57 | + if (picked == INVALID_UINT) { | ||
| 58 | + HCCL_INFO( | ||
| 59 | + "[TopoMatchOneLevel] Rank [%u], no layer with localRanks == userRankSize[%u] (requireHost[%d]).", | ||
| 60 | + topoInfo->userRank, topoInfo->userRankSize, static_cast<int32_t>(requireHost)); | ||
| 61 | + return HcclResult::HCCL_E_NOT_SUPPORT; | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + algHierarchyInfo.infos.resize(1); | ||
| 65 | + algHierarchyInfo.infos[0].resize(1); | ||
| 66 | + algHierarchyInfo.infos[0][0] = physicalLevels[picked].localRanks; | ||
| 67 | + algHierarchyInfo.physicalIdxForAlgoLevels = {{static_cast<PhysicalLevelIndex>(picked)}}; | ||
| 68 | + HCCL_INFO( | ||
| 69 | + "[TopoMatchOneLevel] Rank [%u], userRankSize [%u], physicalIdxForAlgoLevels: [%s].", topoInfo->userRank, | ||
| 70 | + topoInfo->userRankSize, FormatPhysicalIdxForAlgoLevels(algHierarchyInfo.physicalIdxForAlgoLevels).c_str()); | ||
| 71 | + return HcclResult::HCCL_SUCCESS; | ||
| 72 | +} | ||
| 73 | + | ||
| 74 | +} // namespace ops_hccl | ||
| @@ -0,0 +1,32 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2025 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | +namespace ops_hccl { | ||
| 17 | + | ||
| 18 | +class TopoMatchOneLevel : public TopoMatchBaseV2 { | ||
| 19 | +public: | ||
| 20 | + explicit TopoMatchOneLevel(); | ||
| 21 | + ~TopoMatchOneLevel() override; | ||
| 22 | + | ||
| 23 | + std::string Describe() const override { return "Topo Match for One Level Algorithm."; } | ||
| 24 | + | ||
| 25 | + HcclResult MatchTopo( | ||
| 26 | + TopoInfoWithNetLayerDetails* topoInfo, AlgHierarchyInfoForAllLevel& algHierarchyInfo, | ||
| 27 | + const AlgAttrs& profile) override; | ||
| 28 | +}; | ||
| 29 | + | ||
| 30 | +} // namespace ops_hccl | ||
| 31 | + | ||
| 32 | + | ||
| @@ -0,0 +1,124 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2025 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | +namespace ops_hccl { | ||
| 16 | + | ||
| 17 | +namespace { | ||
| 18 | + // 校验 level 对称并取维度:GLOBAL 看 instList 是否全等;LOCAL 视为对称 | ||
| 19 | + HcclResult ValidateLevelAndCalcDim( | ||
| 20 | + u32 levelIdx, const std::vector<PhysicalLevelInfo>& physicalLevels, 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( | ||
| 40 | + const std::vector<PhysicalLevelInfo>& physicalLevels, u32 phys0, u32 phys1, u32 userRankSize, u32 myRank, | ||
| 41 | + u32& d0, u32& d1, u32& d2) | ||
| 42 | + { | ||
| 43 | + u32 level1TotalSize = 0; | ||
| 44 | + bool sym0 = false; | ||
| 45 | + bool sym1 = false; | ||
| 46 | + CHK_RET(ValidateLevelAndCalcDim(phys0, physicalLevels, sym0, d0)); | ||
| 47 | + CHK_RET(ValidateLevelAndCalcDim(phys1, physicalLevels, sym1, level1TotalSize)); | ||
| 48 | + if (!sym0 || !sym1) { | ||
| 49 | + HCCL_INFO( | ||
| 50 | + "[TopoMatchThreeLevel] Rank [%u], asymmetric detected (sym0[%d] sym1[%d]), not support.", myRank, | ||
| 51 | + static_cast<int32_t>(sym0), static_cast<int32_t>(sym1)); | ||
| 52 | + return HcclResult::HCCL_E_NOT_SUPPORT; | ||
| 53 | + } | ||
| 54 | + if (d0 == 0 || level1TotalSize == 0 || level1TotalSize % d0 != 0) { | ||
| 55 | + HCCL_INFO( | ||
| 56 | + "[TopoMatchThreeLevel] Rank [%u], level1TotalSize[%u] not divisible by d0[%u].", myRank, | ||
| 57 | + level1TotalSize, d0); | ||
| 58 | + return HcclResult::HCCL_E_NOT_SUPPORT; | ||
| 59 | + } | ||
| 60 | + d1 = level1TotalSize / d0; | ||
| 61 | + if (userRankSize % d0 != 0 || (userRankSize / d0) % d1 != 0) { | ||
| 62 | + HCCL_INFO( | ||
| 63 | + "[TopoMatchThreeLevel] Rank [%u], userRankSize[%u] not divisible by d0[%u]*d1[%u].", myRank, | ||
| 64 | + userRankSize, d0, d1); | ||
| 65 | + return HcclResult::HCCL_E_NOT_SUPPORT; | ||
| 66 | + } | ||
| 67 | + d2 = userRankSize / d0 / d1; | ||
| 68 | + return HcclResult::HCCL_SUCCESS; | ||
| 69 | + } | ||
| 70 | +} // namespace | ||
| 71 | + | ||
| 72 | +TopoMatchThreeLevel::TopoMatchThreeLevel() {} | ||
| 73 | +TopoMatchThreeLevel::~TopoMatchThreeLevel() {} | ||
| 74 | + | ||
| 75 | +HcclResult TopoMatchThreeLevel::MatchTopo( | ||
| 76 | + TopoInfoWithNetLayerDetails* topoInfo, AlgHierarchyInfoForAllLevel& algHierarchyInfo, const AlgAttrs& profile) | ||
| 77 | +{ | ||
| 78 | + const auto& physicalLevels = topoInfo->physicalLevels; | ||
| 79 | + u32 myRank = topoInfo->userRank; | ||
| 80 | + u32 userRankSize = topoInfo->userRankSize; | ||
| 81 | + if (physicalLevels.empty() || userRankSize == 0 || profile.algoTypes.size() != ALGO_LEVEL_NUM_THREE) { | ||
| 82 | + HCCL_ERROR("[TopoMatchThreeLevel] Rank [%u], invalid input.", myRank); | ||
| 83 | + return HcclResult::HCCL_E_INTERNAL; | ||
| 84 | + } | ||
| 85 | + | ||
| 86 | + // 引擎过滤 + 锚点匹配 + 分段 + 最高层校验 | ||
| 87 | + std::vector<u32> effIdx; | ||
| 88 | + std::vector<u32> pIndices; | ||
| 89 | + CHK_RET(ResolveMapping(physicalLevels, profile, userRankSize, effIdx, pIndices)); | ||
| 90 | + u32 phys0 = effIdx[pIndices[0]]; | ||
| 91 | + u32 phys1 = effIdx[pIndices[1]]; | ||
| 92 | + | ||
| 93 | + // 非对称判定 + 维度计算(ThreeLevel 不支持非对称) | ||
| 94 | + u32 d0 = 0; | ||
| 95 | + u32 d1 = 0; | ||
| 96 | + u32 d2 = 0; | ||
| 97 | + CHK_RET(CalcDimsAndCheckSymmetry(physicalLevels, phys0, phys1, userRankSize, myRank, d0, d1, d2)); | ||
| 98 | + | ||
| 99 | + // 构造 infos;level1 代表环须落在 myRank 所在 level1 instance 内,故 offset 取 instance 基址 + 层内偏移 | ||
| 100 | + std::vector<u32> group0 = physicalLevels[phys0].localRanks; | ||
| 101 | + u32 level1Base = (myRank / (d0 * d1)) * (d0 * d1); | ||
| 102 | + std::vector<u32> group1 = BuildRepresentativeGroup(d0, d1, level1Base + myRank % d0); | ||
| 103 | + std::vector<u32> group2 = BuildRepresentativeGroup(d0 * d1, d2, myRank % (d0 * d1)); | ||
| 104 | + CHK_RET(ValidateGroup(group0, d0, myRank, "level0")); | ||
| 105 | + CHK_RET(ValidateGroup(group1, d1, myRank, "level1")); | ||
| 106 | + CHK_RET(ValidateGroup(group2, d2, myRank, "level2")); | ||
| 107 | + algHierarchyInfo.infos.resize(ALGO_LEVEL_NUM_THREE); | ||
| 108 | + for (u32 i = 0; i < ALGO_LEVEL_NUM_THREE; i++) { | ||
| 109 | + algHierarchyInfo.infos[i].resize(1); | ||
| 110 | + } | ||
| 111 | + algHierarchyInfo.infos[0][0] = std::move(group0); | ||
| 112 | + algHierarchyInfo.infos[1][0] = std::move(group1); | ||
| 113 | + algHierarchyInfo.infos[ALGO_LEVEL_NUM_TWO][0] = std::move(group2); | ||
| 114 | + | ||
| 115 | + // 填充 physicalIdxForAlgoLevels(二级:MeshConcur 双层,普通单层) | ||
| 116 | + CHK_RET(FillPhysicalIdxForAlgoLevels( | ||
| 117 | + physicalLevels, effIdx, pIndices, profile.algoTypes, algHierarchyInfo.physicalIdxForAlgoLevels)); | ||
| 118 | + HCCL_INFO( | ||
| 119 | + "[TopoMatchThreeLevel] Rank [%u], d0[%u] d1[%u] d2[%u], physicalIdxForAlgoLevels: [%s].", myRank, d0, d1, d2, | ||
| 120 | + FormatPhysicalIdxForAlgoLevels(algHierarchyInfo.physicalIdxForAlgoLevels).c_str()); | ||
| 121 | + return HcclResult::HCCL_SUCCESS; | ||
| 122 | +} | ||
| 123 | + | ||
| 124 | +} // namespace ops_hccl | ||
| @@ -0,0 +1,32 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2025 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | +namespace ops_hccl { | ||
| 17 | + | ||
| 18 | +class TopoMatchThreeLevel : public TopoMatchBaseV2 { | ||
| 19 | +public: | ||
| 20 | + explicit TopoMatchThreeLevel(); | ||
| 21 | + ~TopoMatchThreeLevel() override; | ||
| 22 | + | ||
| 23 | + std::string Describe() const override { return "Topo Match for Three Level Algorithm."; } | ||
| 24 | + | ||
| 25 | + HcclResult MatchTopo( | ||
| 26 | + TopoInfoWithNetLayerDetails* topoInfo, AlgHierarchyInfoForAllLevel& algHierarchyInfo, | ||
| 27 | + const AlgAttrs& profile) override; | ||
| 28 | +}; | ||
| 29 | + | ||
| 30 | +} // namespace ops_hccl | ||
| 31 | + | ||
| 32 | + | ||
| @@ -0,0 +1,116 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2025 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | +namespace ops_hccl { | ||
| 16 | + | ||
| 17 | +namespace { | ||
| 18 | + // 计算内层维度 d0:LOCAL 取 localRanks.size();GLOBAL 对称取 localRanks.size(),非对称 GCD 打平 | ||
| 19 | + HcclResult CalcLevel0Dim(const PhysicalLevelInfo& level0, u32 myRank, u32& d0, bool& asymmetric, u32& gcd) | ||
| 20 | + { | ||
| 21 | + if (level0.view == PhysicalLevelView::LOCAL) { | ||
| 22 | + d0 = static_cast<u32>(level0.localRanks.size()); | ||
| 23 | + return HcclResult::HCCL_SUCCESS; | ||
| 24 | + } | ||
| 25 | + if (level0.instSizeListByLayer.empty()) { | ||
| 26 | + HCCL_ERROR("[TopoMatchTwoLevel] netLayer [ref = %u] instSizeListByLayer is empty.", level0.ref.netLayer); | ||
| 27 | + return HcclResult::HCCL_E_INTERNAL; | ||
| 28 | + } | ||
| 29 | + if (IsInstListSymmetric(level0.instSizeListByLayer)) { | ||
| 30 | + d0 = static_cast<u32>(level0.localRanks.size()); | ||
| 31 | + asymmetric = false; | ||
| 32 | + return HcclResult::HCCL_SUCCESS; | ||
| 33 | + } | ||
| 34 | + // GLOBAL 非对称:对 instSizeListByLayer 取 GCD 打平为对称子组 | ||
| 35 | + asymmetric = true; | ||
| 36 | + gcd = CalcGcd(level0.instSizeListByLayer); | ||
| 37 | + HCCL_INFO("[TopoMatchTwoLevel] Rank [%u], asymmetric level0, instList GCD[%u], d0=gcd.", myRank, gcd); | ||
| 38 | + if (gcd == 1) { | ||
| 39 | + HCCL_INFO("[TopoMatchTwoLevel] Rank [%u], asymmetric GCD=1, not support.", myRank); | ||
| 40 | + return HcclResult::HCCL_E_NOT_SUPPORT; | ||
| 41 | + } | ||
| 42 | + d0 = gcd; | ||
| 43 | + return HcclResult::HCCL_SUCCESS; | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + // 构造含 myRank 的内层组;非对称时按 gcd 从 localRanks 切子组 | ||
| 47 | + std::vector<u32> BuildLevel0Group(const PhysicalLevelInfo& level0, u32 myRank, bool asymmetric, u32 gcd) | ||
| 48 | + { | ||
| 49 | + if (!asymmetric) { | ||
| 50 | + return level0.localRanks; | ||
| 51 | + } | ||
| 52 | + const auto& ranks = level0.localRanks; | ||
| 53 | + auto it = std::find(ranks.begin(), ranks.end(), myRank); | ||
| 54 | + if (it == ranks.end()) { | ||
| 55 | + return {}; | ||
| 56 | + } | ||
| 57 | + u32 myIdx = static_cast<u32>(it - ranks.begin()); | ||
| 58 | + u32 startIdx = (myIdx / gcd) * gcd; | ||
| 59 | + u32 endIdx = std::min(startIdx + gcd, static_cast<u32>(ranks.size())); | ||
| 60 | + return std::vector<u32>(ranks.begin() + startIdx, ranks.begin() + endIdx); | ||
| 61 | + } | ||
| 62 | +} // namespace | ||
| 63 | + | ||
| 64 | +TopoMatchTwoLevel::TopoMatchTwoLevel() {} | ||
| 65 | +TopoMatchTwoLevel::~TopoMatchTwoLevel() {} | ||
| 66 | + | ||
| 67 | +HcclResult TopoMatchTwoLevel::MatchTopo( | ||
| 68 | + TopoInfoWithNetLayerDetails* topoInfo, AlgHierarchyInfoForAllLevel& algHierarchyInfo, const AlgAttrs& profile) | ||
| 69 | +{ | ||
| 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( | ||
| 108 | + physicalLevels, effIdx, pIndices, profile.algoTypes, algHierarchyInfo.physicalIdxForAlgoLevels)); | ||
| 109 | + HCCL_INFO( | ||
| 110 | + "[TopoMatchTwoLevel] Rank [%u], d0[%u] d1[%u] asym[%d], physicalIdxForAlgoLevels: [%s].", myRank, d0, d1, | ||
| 111 | + static_cast<int32_t>(asymmetric), | ||
| 112 | + FormatPhysicalIdxForAlgoLevels(algHierarchyInfo.physicalIdxForAlgoLevels).c_str()); | ||
| 113 | + return HcclResult::HCCL_SUCCESS; | ||
| 114 | +} | ||
| 115 | + | ||
| 116 | +} // namespace ops_hccl | ||
| @@ -0,0 +1,32 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2025 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | +namespace ops_hccl { | ||
| 17 | + | ||
| 18 | +class TopoMatchTwoLevel : public TopoMatchBaseV2 { | ||
| 19 | +public: | ||
| 20 | + explicit TopoMatchTwoLevel(); | ||
| 21 | + ~TopoMatchTwoLevel() override; | ||
| 22 | + | ||
| 23 | + std::string Describe() const override { return "Topo Match for Two Level Algorithm."; } | ||
| 24 | + | ||
| 25 | + HcclResult MatchTopo( | ||
| 26 | + TopoInfoWithNetLayerDetails* topoInfo, AlgHierarchyInfoForAllLevel& algHierarchyInfo, | ||
| 27 | + const AlgAttrs& profile) override; | ||
| 28 | +}; | ||
| 29 | + | ||
| 30 | +} // namespace ops_hccl | ||
| 31 | + | ||
| 32 | + | ||
| @@ -9,7 +9,9 @@ | |||
| 9 | */ | 9 | */ |
| 10 | 10 | ||
| 11 | 11 | ||
| 12 | + | ||
| 12 | 13 | ||
| 14 | + | ||
| 13 | 15 | ||
| 14 | 16 | ||
| 15 | 17 | ||
| @@ -24,9 +26,18 @@ template <typename AlgTopoMatch, typename InsAlgTemplate> | |||
| 24 | HcclResult InsV2ReduceScatterVSoleExecutor<AlgTopoMatch, InsAlgTemplate>::CalcAlgHierarchyInfo( | 26 | HcclResult InsV2ReduceScatterVSoleExecutor<AlgTopoMatch, InsAlgTemplate>::CalcAlgHierarchyInfo( |
| 25 | HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo, AlgHierarchyInfoForAllLevel& algHierarchyInfo) | 27 | HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo, AlgHierarchyInfoForAllLevel& algHierarchyInfo) |
| 26 | { | 28 | { |
| 27 | - // 使用topo match计算AlgHierarchyInfoForAllLevel | 29 | + (void)comm; |
| 28 | AlgTopoMatch topoMatch; | 30 | AlgTopoMatch topoMatch; |
| 29 | - CHK_RET(topoMatch.MatchTopo(comm, topoInfo, algHierarchyInfo)); | 31 | + CHK_RET(topoMatch.MatchTopo(topoInfo, algHierarchyInfo, AlgAttrs{})); |
| 32 | + return HCCL_SUCCESS; | ||
| 33 | +} | ||
| 34 | + | ||
| 35 | +template <typename AlgTopoMatch, typename InsAlgTemplate> | ||
| 36 | +HcclResult InsV2ReduceScatterVSoleExecutor<AlgTopoMatch, InsAlgTemplate>::CalcAlgHierarchyInfoV2( | ||
| 37 | + TopoInfoWithNetLayerDetails* topoInfo, AlgHierarchyInfoForAllLevel& algHierarchyInfo, const AlgAttrs& algAttrs) | ||
| 38 | +{ | ||
| 39 | + AlgTopoMatch topoMatch; | ||
| 40 | + CHK_RET(topoMatch.MatchTopo(topoInfo, algHierarchyInfo, algAttrs)); | ||
| 30 | return HCCL_SUCCESS; | 41 | return HCCL_SUCCESS; |
| 31 | } | 42 | } |
| 32 | 43 | ||
| @@ -181,13 +192,16 @@ HcclResult InsV2ReduceScatterVSoleExecutor<AlgTopoMatch, InsAlgTemplate>::Orches | |||
| 181 | 192 | ||
| 182 | // 第二个参数是Reduce Scatter的template文件 | 193 | // 第二个参数是Reduce Scatter的template文件 |
| 183 | REGISTER_EXEC_V2( | 194 | REGISTER_EXEC_V2( |
| 184 | - HcclCMDType::HCCL_CMD_REDUCE_SCATTER_V, AicpuReduceScatterVSoleMesh, InsV2ReduceScatterVSoleExecutor, TopoMatch1D, | 195 | + HcclCMDType::HCCL_CMD_REDUCE_SCATTER_V, AicpuReduceScatterVSoleMesh, InsV2ReduceScatterVSoleExecutor, |
| 185 | - InsTempReduceScatterVMesh1D); | 196 | + TopoMatchOneLevel, InsTempReduceScatterVMesh1D); |
| 197 | +REGISTER_ALG_ATTRS(AicpuReduceScatterVSoleMesh, topo.maxTopoLevelNum = 3; topo.supportLevel0Topos = LEVEL0_TOPO_ANY;); | ||
| 186 | 198 | ||
| 187 | 199 | ||
| 188 | REGISTER_EXEC_V2( | 200 | REGISTER_EXEC_V2( |
| 189 | HcclCMDType::HCCL_CMD_REDUCE_SCATTER_V, CcuSchedReduceScatterVSoleMesh, InsV2ReduceScatterVSoleExecutor, | 201 | HcclCMDType::HCCL_CMD_REDUCE_SCATTER_V, CcuSchedReduceScatterVSoleMesh, InsV2ReduceScatterVSoleExecutor, |
| 190 | - TopoMatch1D, CcuTempReduceScatterVMesh1DMem2Mem); | 202 | + TopoMatchOneLevel, CcuTempReduceScatterVMesh1DMem2Mem); |
| 203 | +REGISTER_ALG_ATTRS(CcuSchedReduceScatterVSoleMesh, topo.maxTopoLevelNum = 1; | ||
| 204 | + topo.supportLevel0Topos = LEVEL0_TOPO_MESH_1D; op.isSupportInplace = false;); | ||
| 191 | 205 | ||
| 192 | 206 | ||
| 193 | } // namespace ops_hccl | 207 | } // namespace ops_hccl |
| @@ -12,7 +12,7 @@ | |||
| 12 | 12 | ||
| 13 | 13 | ||
| 14 | 14 | ||
| 15 | -#include "topo_match_1d.h" | 15 | +#include "topo_match_one_level.h" |
| 16 | 16 | ||
| 17 | 17 | ||
| 18 | namespace ops_hccl { | 18 | namespace ops_hccl { |
| @@ -32,6 +32,10 @@ public: | |||
| 32 | HcclResult CalcAlgHierarchyInfo( | 32 | HcclResult CalcAlgHierarchyInfo( |
| 33 | HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo, AlgHierarchyInfoForAllLevel& algHierarchyInfo) override; | 33 | HcclComm comm, TopoInfoWithNetLayerDetails* topoInfo, AlgHierarchyInfoForAllLevel& algHierarchyInfo) override; |
| 34 | 34 | ||
| 35 | + HcclResult CalcAlgHierarchyInfoV2( | ||
| 36 | + TopoInfoWithNetLayerDetails* topoInfo, AlgHierarchyInfoForAllLevel& algHierarchyInfo, | ||
| 37 | + const AlgAttrs& algAttrs) override; | ||
| 38 | + | ||
| 35 | protected: | 39 | protected: |
| 36 | /* *************** 算法编排 *************** */ | 40 | /* *************** 算法编排 *************** */ |
| 37 | HcclResult OrchestrateLoop(const OpParam& param, const AlgResourceCtxSerializable& resCtx); | 41 | HcclResult OrchestrateLoop(const OpParam& param, const AlgResourceCtxSerializable& resCtx); |
| @@ -167,6 +167,11 @@ if(NOT HCCL_CANN_COMPAT_850) | |||
| 167 | ${CMAKE_CURRENT_SOURCE_DIR}/ops/op_common/topo/topo_match_pcie_mix.cc | 167 | ${CMAKE_CURRENT_SOURCE_DIR}/ops/op_common/topo/topo_match_pcie_mix.cc |
| 168 | ${CMAKE_CURRENT_SOURCE_DIR}/ops/op_common/topo/topo_match_3_level.cc | 168 | ${CMAKE_CURRENT_SOURCE_DIR}/ops/op_common/topo/topo_match_3_level.cc |
| 169 | ${CMAKE_CURRENT_SOURCE_DIR}/ops/op_common/topo/topo_match_squeeze_2d.cc | 169 | ${CMAKE_CURRENT_SOURCE_DIR}/ops/op_common/topo/topo_match_squeeze_2d.cc |
| 170 | + ${CMAKE_CURRENT_SOURCE_DIR}/ops/op_common/topo/topo_match_base_v2.cc | ||
| 171 | + ${CMAKE_CURRENT_SOURCE_DIR}/ops/op_common/topo/topo_match_one_level.cc | ||
| 172 | + ${CMAKE_CURRENT_SOURCE_DIR}/ops/op_common/topo/topo_match_two_level.cc | ||
| 173 | + ${CMAKE_CURRENT_SOURCE_DIR}/ops/op_common/topo/topo_match_three_level.cc | ||
| 174 | + ${CMAKE_CURRENT_SOURCE_DIR}/ops/op_common/topo/topo_match_concurrent_v2.cc | ||
| 170 | ${CMAKE_CURRENT_SOURCE_DIR}/ops/reduce_scatter/executor/ins_reduce_scatter_concurrent_executor.cc | 175 | ${CMAKE_CURRENT_SOURCE_DIR}/ops/reduce_scatter/executor/ins_reduce_scatter_concurrent_executor.cc |
| 171 | ${CMAKE_CURRENT_SOURCE_DIR}/ops/all_gather/executor/ins_v2_all_gather_concurrent_executor.cc | 176 | ${CMAKE_CURRENT_SOURCE_DIR}/ops/all_gather/executor/ins_v2_all_gather_concurrent_executor.cc |
| 172 | ${CMAKE_CURRENT_SOURCE_DIR}/ops/all_to_all_v/executor/ins_v2_all_to_all_concurrent_executor.cc | 177 | ${CMAKE_CURRENT_SOURCE_DIR}/ops/all_to_all_v/executor/ins_v2_all_to_all_concurrent_executor.cc |
| @@ -334,6 +334,15 @@ aclError aclrtRecordNotify(aclrtNotify notify, aclrtStream stream) | |||
| 334 | 334 | ||
| 335 | aclError aclrtGetDeviceInfo(uint32_t deviceId, aclrtDevAttr attr, int64_t* value) | 335 | aclError aclrtGetDeviceInfo(uint32_t deviceId, aclrtDevAttr attr, int64_t* value) |
| 336 | { | 336 | { |
| 337 | +// 老CANN的acl_rt.h没有这个枚举, 探测与它同批引入的宏, 口径同adapter_acl.h | ||
| 338 | + | ||
| 339 | + // 本函数其余分支不回写*value。调用方普遍把出参初始化为0, 而ACL_DEVICE_FORM_FACTOR_POD恰好是0, | ||
| 340 | + // 不显式给出非POD取值的话, ST会把每个用例都建模成POD机型 | ||
| 341 | + if (attr == ACL_DEV_ATTR_DEVICE_FORM_FACTOR) { | ||
| 342 | + *value = ACL_DEVICE_FORM_FACTOR_A_X; | ||
| 343 | + return ACL_SUCCESS; | ||
| 344 | + } | ||
| 345 | + | ||
| 337 | HCCL_WARNING("[%s] not support.", __func__); | 346 | HCCL_WARNING("[%s] not support.", __func__); |
| 338 | return ACL_SUCCESS; | 347 | return ACL_SUCCESS; |
| 339 | } | 348 | } |
| @@ -344,6 +353,13 @@ aclError aclrtGetLogicDevIdByPhyDevId(int32_t phyDevId, int32_t* const logicDevI | |||
| 344 | return ACL_SUCCESS; | 353 | return ACL_SUCCESS; |
| 345 | } | 354 | } |
| 346 | 355 | ||
| 356 | +aclError aclrtGetLogicDevIdByUserDevId(const int32_t userDevId, int32_t* const logicDevId) | ||
| 357 | +{ | ||
| 358 | + // SimWorld不区分user/logic设备号, aclrtGetDevice直接返回curr_dev_id, 这里原样透传 | ||
| 359 | + *logicDevId = userDevId; | ||
| 360 | + return ACL_SUCCESS; | ||
| 361 | +} | ||
| 362 | + | ||
| 347 | aclError aclrtGetPhyDevIdByLogicDevId(const int32_t logicDevId, int32_t* const phyDevId) | 363 | aclError aclrtGetPhyDevIdByLogicDevId(const int32_t logicDevId, int32_t* const phyDevId) |
| 348 | { | 364 | { |
| 349 | auto npuPos = HcclSim::SimWorld::Global()->GetNpuPosByRankId(curr_dev_id); | 365 | auto npuPos = HcclSim::SimWorld::Global()->GetNpuPosByRankId(curr_dev_id); |