Line data Source code
1 : /**
2 : * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3 : * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4 : * CANN Open Software License Agreement Version 2.0 (the "License").
5 : * Please refer to the License for details. You may not use this file except in compliance with the License.
6 : * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7 : * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8 : * See LICENSE in the root of the software repository for the full text of the License.
9 : */
10 :
11 : #include "topo_match_concurr_mesh_nhr.h"
12 :
13 : namespace Hccl {
14 0 : TopoMatchConcurrMeshNHR::TopoMatchConcurrMeshNHR(
15 0 : const RankId vRank, const u32 rankSize, const RankGraph* rankGraph, const DevType devType)
16 0 : : TopoMatchBase(vRank, rankSize, rankGraph, devType)
17 0 : {}
18 :
19 0 : TopoMatchConcurrMeshNHR::~TopoMatchConcurrMeshNHR() {}
20 :
21 0 : HcclResult TopoMatchConcurrMeshNHR::MatchTopo(
22 : std::vector<std::vector<std::vector<RankId>>>& vTopo, std::vector<std::vector<RankId>>& virtRanks,
23 : std::vector<std::map<RankId, u32>>& virtRankMap)
24 : {
25 : // 获取并校验当前通信层数
26 0 : std::set<u32> levelSet = rankGraph_->GetLevels(myRank_);
27 0 : CHK_PRT_RET(
28 : (levelSet.size() < COMM_LEVEL_SIZE_2),
29 : HCCL_ERROR(
30 : "[CollAlgFactory] [TopoMatchConcurrMeshNHR] Rank [%d], Invalid virtual topo. levelSet[%u]", myRank_,
31 : levelSet.size()),
32 : HcclResult::HCCL_E_PARA);
33 :
34 0 : rankGraph_->Dump();
35 :
36 : // 获取 level0 Pod Topo 信息
37 0 : const NetInstance* netInstance = rankGraph_->GetNetInstanceByRankId(0, myRank_);
38 0 : if (netInstance == nullptr) {
39 0 : HCCL_ERROR("TopoMatchConcurrMeshNHR::MatchTopo netInstance is nullptr");
40 0 : return HcclResult::HCCL_E_PTR;
41 : }
42 0 : const u32 rankSizeLevel0 = netInstance->GetRankSize();
43 0 : std::set<RankId> rankSetLevel0 = netInstance->GetRankIds();
44 :
45 : // 校验 level0 Pod Topo 是否符合 m x n 要求
46 0 : CHK_RET(CalcRankOnSamePlaneOfR0(rankOnSameBoardVector_, rankOnSameSlotVector_, numRanksPerBoard_));
47 0 : const auto minmaxPair = std::minmax_element(numRanksPerBoard_.begin(), numRanksPerBoard_.end());
48 0 : u32 minNumRankPerBoard = *minmaxPair.first;
49 0 : u32 maxNumRankPerBoard = *minmaxPair.second;
50 0 : CHK_PRT_RET(
51 : (minNumRankPerBoard != maxNumRankPerBoard),
52 : HCCL_ERROR(
53 : "[CollAlgFactory] [TopoMatchConcurrMeshNHR] Rank [%d], Invalid virtual topo for "
54 : "multi-dimensional concurrent mesh, min numRanksPerBoard_[%u], max numRanksPerBoard_[%u].",
55 : myRank_, minNumRankPerBoard, maxNumRankPerBoard),
56 : HcclResult::HCCL_E_PARA);
57 :
58 0 : CHK_PRT_RET(
59 : ((rankSize_ == 1) || (numRanksPerBoard_[0] * numRanksPerBoard_.size() != rankSizeLevel0)),
60 : HCCL_ERROR(
61 : "[CollAlgFactory] [TopoMatchConcurrMeshNHR] Rank [%d], Invalid virtual topo for "
62 : "multi-dimensional concurrent mesh algorithm with rankSize [%u], rankSizeLevel0 [%u], "
63 : "ranksPerBoard [%u], ranksPerSlot [%u].",
64 : myRank_, rankSize_, rankSizeLevel0, numRanksPerBoard_[0], numRanksPerBoard_.size()),
65 : HcclResult::HCCL_E_PARA);
66 :
67 : // 在全局视角下计算 level0 sub-communicator size
68 0 : std::vector<u32> instSizeList; // rank num on each pod
69 : u32 listSize; // pod num
70 0 : rankGraph_->GetNetInstanceList(0, instSizeList, listSize); // global view
71 0 : HCCL_DEBUG(
72 : "[CollAlgFactory] [TopoMatchConcurrMeshNHR] Rank [%d], instSizeList[%u]=[%s]", myRank_, instSizeList.size(),
73 : PrintVector<u32>(instSizeList).c_str());
74 :
75 0 : if (!CheckSymmetric(instSizeList)) {
76 : // 非对称情形
77 0 : HCCL_DEBUG("[CollAlgFactory] [TopoMatchConcurrMeshNHR] Rank [%d], Asymmetric MatchTopo.", myRank_);
78 0 : u32 gcdInstSize = GcdMultiple(instSizeList);
79 :
80 : // 计算 level 0 子通信域
81 0 : CHK_RET(GenerateLevel0(rankSetLevel0, gcdInstSize, myRank_, vTopo, virtRanks));
82 :
83 : // 计算 level 1 子通信域
84 0 : const NetInstance* netInstanceL1 = rankGraph_->GetNetInstanceByRankId(1, myRank_);
85 0 : if (netInstanceL1 == nullptr) {
86 0 : HCCL_ERROR("TopoMatchConcurrMeshNHR::MatchTopo netInstanceL1 is nullptr");
87 0 : return HcclResult::HCCL_E_PTR;
88 : }
89 0 : std::set<RankId> rankSetLevel1 = netInstanceL1->GetRankIds();
90 :
91 0 : CHK_RET(GenerateLevel1(rankSetLevel1, gcdInstSize, myRank_, vTopo, virtRanks));
92 :
93 0 : HCCL_DEBUG(
94 : "[CollAlgFactory] [TopoMatchConcurrMeshNHR] Rank [%d], level0 & level1 virtRanks=[%s] ", myRank_,
95 : PrintMatrix<RankId>(virtRanks).c_str());
96 0 : HCCL_DEBUG(
97 : "[CollAlgFactory] [TopoMatchConcurrMeshNHR] Rank [%d], level0 & level1 vTopo=[%s] ", myRank_,
98 : PrintTensor<RankId>(vTopo).c_str());
99 0 : } else {
100 : // 对称情形
101 0 : HCCL_DEBUG("[CollAlgFactory] [TopoMatchConcurrMeshNHR] Rank [%d], Symmetric MatchTopo.", myRank_);
102 :
103 0 : u32 myLocalId = rankGraph_->GetLocalId(myRank_);
104 0 : rankOnSameBoard_ = rankOnSameBoardVector_[myLocalId / RANK_SIZE_EIGHT];
105 0 : rankOnSameSlot_ = rankOnSameSlotVector_[myLocalId % RANK_SIZE_EIGHT];
106 0 : if ((rankOnSameBoard_.size() == 1) || (rankOnSameSlot_.size() == 1)) {
107 0 : HCCL_DEBUG(
108 : "[CollAlgFactory] [TopoMatchConcurrMeshNHR] Rank [%d], virtual topo with rankSize [%u], ranksPerBoard "
109 : "[%u], ranksPerSlot [%u]. 1-D Mesh algorithm should be adopted.",
110 : myRank_, rankSize_, rankOnSameBoard_.size(), rankOnSameSlot_.size());
111 : }
112 :
113 0 : sort(rankOnSameBoard_.begin(), rankOnSameBoard_.end());
114 0 : sort(rankOnSameSlot_.begin(), rankOnSameSlot_.end());
115 :
116 0 : Matrix<RankId> rankOnSamePod;
117 0 : rankOnSamePod.push_back(rankOnSameBoard_);
118 0 : rankOnSamePod.push_back(rankOnSameSlot_);
119 0 : vTopo.push_back(rankOnSamePod);
120 0 : virtRanks.push_back({rankSetLevel0.cbegin(), rankSetLevel0.cend()});
121 :
122 : // 获取 level1 Topo 信息
123 0 : const NetInstance* netInstanceL1 = rankGraph_->GetNetInstanceByRankId(1, myRank_);
124 0 : if (netInstanceL1 == nullptr) {
125 0 : HCCL_ERROR("TopoMatchConcurrMeshNHR::MatchTopo netInstanceL1 is nullptr");
126 0 : return HcclResult::HCCL_E_PTR;
127 : }
128 0 : std::set<RankId> rankSetLevel1 = netInstanceL1->GetRankIds();
129 :
130 : // 它要求 rankId 是连续的, 每隔 rankSizeLevel0 取一个
131 0 : CHK_RET(GenerateLevel1(rankSetLevel1, rankSizeLevel0, myRank_, vTopo, virtRanks));
132 :
133 0 : HCCL_DEBUG(
134 : "[CollAlgFactory] [TopoMatchConcurrMeshNHR] Rank [%d], level0 & level1 virtRanks=[%s] ", myRank_,
135 : PrintMatrix<RankId>(virtRanks).c_str());
136 0 : HCCL_DEBUG(
137 : "[CollAlgFactory] [TopoMatchConcurrMeshNHR] Rank [%d], level0 & level1 vTopo=[%s] ", myRank_,
138 : PrintTensor<RankId>(vTopo).c_str());
139 0 : }
140 :
141 : // 子通信域计算完毕, 生成 virtRankMap
142 0 : CHK_PRT_RET(
143 : GenVirtRankMappingMultiLevel(virtRanks, virtRankMap) != HcclResult::HCCL_SUCCESS,
144 : HCCL_ERROR("[CollAlgFactory] [TopoMatchConcurrMeshNHR] Rank [%d], Fail to generate virtRankMapping.", myRank_),
145 : HcclResult::HCCL_E_INTERNAL);
146 :
147 0 : return HcclResult::HCCL_SUCCESS;
148 0 : }
149 :
150 0 : HcclResult TopoMatchConcurrMeshNHR::GenerateLevel0(
151 : const std::set<RankId>& rankSetLevel0, u32 gcdInstSize, RankId rankId,
152 : std::vector<std::vector<std::vector<RankId>>>& vTopo, std::vector<std::vector<RankId>>& virtRanks)
153 : {
154 : // 获取 my pod size, 计算 level0 subcommunicator size
155 : (void)rankId;
156 0 : u32 dim0Size = numRanksPerBoard_.at(0);
157 0 : u32 gcdDim0Size = GcdTwo(gcdInstSize, dim0Size);
158 0 : u32 gcdDim1Size = gcdInstSize / gcdDim0Size;
159 :
160 : // 维数校验 (2D)
161 0 : CHK_PRT_RET(
162 : (gcdDim0Size == 1) || (gcdDim1Size == 1), // 1D case
163 : HCCL_ERROR(
164 : "[CollAlgFactory] [TopoMatchConcurrMeshNHR] Rank [%d], Pod Topo Shape != 2D."
165 : "gcdDim0Size [%u], gcdDim1Size [%u]",
166 : myRank_, gcdDim0Size, gcdDim1Size),
167 : HcclResult::HCCL_E_PARA);
168 :
169 : // 判断 level 0 topo 是否满足 rankId 连续限制
170 0 : bool isRankIdContinue = true;
171 0 : if (gcdDim0Size < dim0Size && gcdDim1Size > 1) {
172 0 : isRankIdContinue = false;
173 : }
174 :
175 0 : if (!isRankIdContinue) {
176 0 : HCCL_ERROR("RankId [%d]. Checker does not support nhr + nhr algorithm validation.", myRank_);
177 0 : RankId minRankId = *rankSetLevel0.cbegin();
178 0 : RankId relRankId = myRank_ - minRankId;
179 0 : u32 groupId = relRankId / gcdInstSize;
180 :
181 0 : const u32 startOffset = groupId * gcdInstSize;
182 0 : const u32 totalSize = rankSetLevel0.size();
183 0 : auto startIt = rankSetLevel0.cbegin();
184 : std::advance(startIt, startOffset);
185 :
186 0 : const u32 elementsToTake = std::min(gcdInstSize, totalSize - startOffset);
187 0 : auto endIt = startIt;
188 : std::advance(endIt, elementsToTake);
189 :
190 0 : std::vector<RankId> level0Ranks = std::vector<RankId>(startIt, endIt);
191 0 : vTopo.push_back({level0Ranks});
192 0 : virtRanks.push_back(level0Ranks);
193 0 : return HcclResult::HCCL_SUCCESS;
194 0 : }
195 :
196 0 : std::vector<RankId> rankVecLevel0 = std::vector<RankId>(rankSetLevel0.cbegin(), rankSetLevel0.cend());
197 0 : Matrix<RankId> rankMatLevel0 = {};
198 0 : auto startIt = rankVecLevel0.cbegin();
199 0 : auto endIt = rankVecLevel0.cend();
200 0 : while (startIt != endIt) {
201 0 : auto currentEndIt = startIt;
202 0 : std::advance(currentEndIt, std::min(dim0Size, static_cast<u32>(std::distance(startIt, endIt))));
203 0 : rankMatLevel0.emplace_back(startIt, currentEndIt);
204 0 : startIt = currentEndIt;
205 : }
206 :
207 0 : std::vector<RankId> level0Ranks;
208 0 : Matrix<RankId> level0Topo;
209 0 : CHK_RET(FindLevel0Block(rankMatLevel0, gcdDim1Size, gcdDim0Size, myRank_, level0Ranks, level0Topo));
210 :
211 0 : CHK_PRT_RET(
212 : (level0Topo[0].size() != gcdDim0Size || level0Topo[1].size() != gcdDim1Size),
213 : HCCL_ERROR(
214 : "[CollAlgFactory] [TopoMatchConcurrMeshNHR] Rank [%d], Invalid level0Topo size[%zu][%zu]", myRank_,
215 : level0Topo[0].size(), level0Topo[1].size()),
216 : HcclResult::HCCL_E_PARA);
217 0 : vTopo.push_back(level0Topo);
218 0 : virtRanks.push_back(level0Ranks);
219 :
220 0 : return HcclResult::HCCL_SUCCESS;
221 0 : }
222 :
223 0 : HcclResult TopoMatchConcurrMeshNHR::FindLevel0Block(
224 : const Matrix<RankId>& podTopo, u32 subDim0Size, u32 subDim1Size, u32 myRank, std::vector<RankId>& subRankList,
225 : Matrix<RankId>& subRankTopo) const
226 : {
227 0 : CHK_PRT_RET(
228 : (podTopo.empty() || podTopo[0].empty() || subDim0Size == 0 || subDim1Size == 0),
229 : HCCL_ERROR("[CollAlgFactory] [TopoMatchConcurrMeshNHR] Rank [%d], empty podTopo or invalid subDim.", myRank_),
230 : HcclResult::HCCL_E_PARA);
231 0 : const u32 totalRows = podTopo.size();
232 0 : const u32 totalCols = podTopo[0].size();
233 0 : CHK_PRT_RET(
234 : (totalRows % subDim0Size != 0 || totalCols % subDim1Size != 0),
235 : HCCL_ERROR("[CollAlgFactory] [TopoMatchConcurrMeshNHR] Rank [%d], invalid subDim.", myRank_),
236 : HcclResult::HCCL_E_PARA);
237 :
238 0 : u32 targetRow = -1;
239 0 : u32 targetCol = -1;
240 0 : CHK_RET(FindMyRankLocation(podTopo, myRank, targetRow, targetCol)); // 查找 myRank 在 podTopo 中坐标
241 :
242 : // 计算 podTopo 块信息
243 0 : const u32 blockRow = targetRow / subDim0Size;
244 0 : const u32 blockCol = targetCol / subDim1Size;
245 :
246 : // 计算目标子块编号和起始索引
247 0 : const u32 startRow = blockRow * subDim0Size;
248 0 : const u32 startCol = blockCol * subDim1Size;
249 :
250 0 : CHK_RET(ExtractLevel0Block(
251 : podTopo, startRow, startCol, targetRow, targetCol, subDim0Size, subDim1Size, subRankList, subRankTopo));
252 :
253 0 : return HcclResult::HCCL_SUCCESS;
254 : }
255 :
256 : HcclResult
257 0 : TopoMatchConcurrMeshNHR::FindMyRankLocation(const Matrix<RankId>& podTopo, u32 myRank, u32& row, u32& col) const
258 : {
259 0 : row = -1;
260 0 : col = -1; // 初始化为非法值
261 0 : for (u32 r = 0; r < podTopo.size(); r++) {
262 0 : auto it = std::find(podTopo[r].cbegin(), podTopo[r].cend(), myRank);
263 0 : if (it != podTopo[r].cend()) {
264 0 : row = r;
265 0 : col = std::distance(podTopo[r].cbegin(), it);
266 0 : return HcclResult::HCCL_SUCCESS;
267 : }
268 : }
269 0 : return HcclResult::HCCL_E_PARA;
270 : }
271 :
272 0 : HcclResult TopoMatchConcurrMeshNHR::ExtractLevel0Block(
273 : const Matrix<RankId>& podTopo, u32 startRow, u32 startCol, u32 targetRow, u32 targetCol, u32 subDim0Size,
274 : u32 subDim1Size, std::vector<RankId>& subRankList, Matrix<RankId>& subRankTopo) const
275 : {
276 0 : subRankList.clear();
277 0 : subRankList.reserve(subDim0Size * subDim1Size);
278 0 : subRankTopo.clear();
279 :
280 0 : const u32 targetRelRow = targetRow - startRow; // myRank 在子块中的相对行 = 绝对行 - 子块起始行
281 0 : const u32 targetRelCol = targetCol - startCol; // myRank 在子块中的相对列 = 绝对列 - 子块起始列
282 :
283 : // 校验相对行&列在 podTopo 范围内
284 0 : const u32 totalRows = podTopo.size();
285 0 : const u32 totalCols = podTopo[0].size();
286 0 : CHK_PRT_RET(
287 : targetRelRow >= totalRows,
288 : HCCL_ERROR(
289 : "[CollAlgFactory] [TopoMatchConcurrMeshNHR] Rank [%d], Invalid targetRelRow[%u], "
290 : "totalRows[%u]",
291 : myRank_, targetRelRow, totalRows),
292 : HcclResult::HCCL_E_PARA);
293 0 : CHK_PRT_RET(
294 : targetRelCol >= totalCols,
295 : HCCL_ERROR(
296 : "[CollAlgFactory] [TopoMatchConcurrMeshNHR] Rank [%d], Invalid targetRelCol[%u], "
297 : "totalCols[%u]",
298 : myRank_, targetRelCol, totalCols),
299 : HcclResult::HCCL_E_PARA);
300 :
301 0 : std::vector<RankId> targetRowVec;
302 0 : std::vector<RankId> targetColVec;
303 0 : targetRowVec.reserve(subDim1Size);
304 0 : targetColVec.reserve(subDim0Size);
305 :
306 0 : auto matRowStartIt = podTopo.cbegin() + startRow; // 使用迭代器算术获取起始行迭代器
307 :
308 : // 循环 subDim0Size 次,每次处理一行
309 0 : for (u32 r = 0; r < subDim0Size; r++) {
310 0 : const auto& sourceRow = *(matRowStartIt + r); // 获取当前行的常量引用
311 : // 计算该行的列坐标起始
312 0 : auto startIt = sourceRow.cbegin() + startCol;
313 0 : auto endIt = sourceRow.cbegin() + startCol + subDim1Size;
314 :
315 0 : subRankList.insert(subRankList.end(), startIt, endIt);
316 :
317 0 : if (r == targetRelRow) {
318 0 : targetRowVec.insert(targetRowVec.end(), startIt, endIt);
319 : }
320 :
321 0 : auto colIt = sourceRow.cbegin() + startCol + targetRelCol;
322 0 : targetColVec.push_back(*colIt);
323 : }
324 :
325 0 : std::sort(targetRowVec.begin(), targetRowVec.end());
326 0 : std::sort(targetColVec.begin(), targetColVec.end());
327 :
328 0 : subRankTopo.emplace_back(std::move(targetRowVec));
329 0 : subRankTopo.emplace_back(std::move(targetColVec));
330 :
331 0 : return HcclResult::HCCL_SUCCESS;
332 0 : }
333 :
334 0 : bool TopoMatchConcurrMeshNHR::CheckSymmetric(std::vector<u32>& values) const
335 : {
336 0 : const auto minmaxPair = std::minmax_element(values.begin(), values.end());
337 0 : u32 minValue = *minmaxPair.first;
338 0 : u32 maxValue = *minmaxPair.second;
339 :
340 0 : return (minValue == maxValue);
341 : }
342 : } // namespace Hccl
|