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 "detour_service.h"
12 : #include "env_config/env_config_v2.h"
13 : #include "detour_rules.h"
14 : #include "not_support_exception.h"
15 :
16 : namespace Hccl {
17 :
18 : using namespace std;
19 :
20 : constexpr u32 DETOUR_NODE_NUM = 8;
21 : constexpr u32 DETOUR_NODE_NUM_2P = 2;
22 : constexpr u32 DETOUR_NODE_NUM_4P = 4;
23 :
24 26 : DetourService& DetourService::GetInstance()
25 : {
26 26 : static DetourService detourService(PhyTopo::GetInstance().get());
27 26 : return detourService;
28 : }
29 :
30 2 : DetourService::DetourService(const PhyTopo* phyTopo) { this->phyTopo = phyTopo; }
31 :
32 : struct DetourData {
33 : NodeId detourPhyPeerId{0};
34 : NodeId srcPhyPeerId{0};
35 : NodeId dstPhyPeerId{0};
36 : shared_ptr<NetInstance::Peer> srcNetInstPeer{nullptr};
37 : shared_ptr<NetInstance::Peer> dstNetInstPeer{nullptr};
38 : };
39 :
40 : vector<shared_ptr<PhyTopo::Link>>
41 0 : GetLinks(NodeId srcId, NodeId dstId, const shared_ptr<Graph<PhyTopo::Node, PhyTopo::Link>>& phyTopoGraph)
42 : {
43 0 : vector<shared_ptr<PhyTopo::Link>> links;
44 0 : if (phyTopoGraph == nullptr) {
45 0 : THROW<NullPtrException>(StringFormat("[GetLinks] phyTopoGraphis nullptr"));
46 : }
47 : // 统一物理图中仅 P2P 边可作为绕路候选。
48 0 : phyTopoGraph->TraverseEdge(srcId, dstId, [&](shared_ptr<PhyTopo::Link> link) {
49 0 : if (link != nullptr && link->GetType() == LinkType::PEER2PEER) {
50 0 : links.emplace_back(link);
51 0 : return;
52 : }
53 : });
54 :
55 0 : return links;
56 0 : }
57 :
58 0 : void AddDetourLink(
59 : NetInstance* innerNetInst, const DetourData& data,
60 : const shared_ptr<Graph<PhyTopo::Node, PhyTopo::Link>>& phyTopoGraph,
61 : [[maybe_unused]] const RankTableInfo* rankTable)
62 : {
63 0 : vector<shared_ptr<PhyTopo::Link>> src2detVec = GetLinks(data.srcPhyPeerId, data.detourPhyPeerId, phyTopoGraph);
64 0 : vector<shared_ptr<PhyTopo::Link>> det2dstVec = GetLinks(data.detourPhyPeerId, data.dstPhyPeerId, phyTopoGraph);
65 0 : if (src2detVec.size() == 0 || det2dstVec.size() == 0) {
66 0 : return;
67 : }
68 0 : if (innerNetInst == nullptr) {
69 0 : THROW<NullPtrException>(StringFormat("[AddDetourLink] innerGroup is nullptr"));
70 : }
71 0 : if (data.srcNetInstPeer == nullptr || data.dstNetInstPeer == nullptr) {
72 0 : THROW<NullPtrException>(
73 0 : StringFormat("[DetourService][InsertDetourLinks][AddDetourLink] virtual peer is nullptr"));
74 : }
75 0 : u32 hop = 2; // 目前绕路hop一定是2
76 0 : for (const auto& src2detLink : src2detVec) {
77 0 : for (const auto& det2dstLink : det2dstVec) {
78 0 : LinkType linkType = src2detLink->GetType();
79 0 : std::set<LinkProtocol> linkProtocols = src2detLink->GetLinkProtocols();
80 0 : if (linkType != det2dstLink->GetType()) {
81 0 : HCCL_WARNING(
82 : "[DetourService][InsertDetourLinks][AddDetourLink] src2det[%s] det2dst[%s] linkType no match",
83 : linkType.Describe().c_str(), det2dstLink->GetType().Describe().c_str());
84 0 : continue;
85 0 : };
86 : // todo 是不是应该改为判断两个set是否有交集,取绕路的协议集合? 修改了一下,llt再check一下
87 0 : std::set<LinkProtocol> newLinkProtocols;
88 0 : std::set_intersection(
89 0 : linkProtocols.begin(), linkProtocols.end(), det2dstLink->GetLinkProtocols().begin(),
90 0 : det2dstLink->GetLinkProtocols().end(), std::inserter(newLinkProtocols, newLinkProtocols.begin()));
91 :
92 0 : if (newLinkProtocols.empty()) {
93 : // todo 先改编译,后面再实现日志打印
94 0 : continue;
95 : };
96 :
97 : // 从Link中获取端口集合
98 0 : if (src2detLink->GetSourceIFace() == nullptr || det2dstLink->GetTargetIFace() == nullptr) {
99 0 : THROW<InvalidParamsException>(
100 : "[DetourService][InsertDetourLinks][AddDetourLink] source ConnInterface is nullptr");
101 : }
102 0 : std::set<string> src2detPorts = src2detLink->GetSourceIFace()->GetPorts();
103 0 : std::set<string> det2dstPorts = det2dstLink->GetTargetIFace()->GetPorts();
104 0 : if (src2detPorts.size() != 1 || det2dstPorts.size() != 1) {
105 0 : HCCL_WARNING(
106 : "[DetourService][InsertDetourLinks][AddDetourLink] Ignore unmatched P2P candidate, "
107 : "srcPortNum[%zu] dstPortNum[%zu]",
108 : src2detPorts.size(), det2dstPorts.size());
109 0 : continue;
110 0 : }
111 :
112 : // 绕路两端口均须属于 RankTable layer 0。
113 0 : IpAddress src2detAddr;
114 0 : IpAddress det2dstAddr;
115 0 : if (!data.srcNetInstPeer->TryGetLayer0Address(*src2detPorts.begin(), src2detAddr)
116 0 : || !data.dstNetInstPeer->TryGetLayer0Address(*det2dstPorts.begin(), det2dstAddr)) {
117 0 : HCCL_WARNING("[DetourService][InsertDetourLinks][AddDetourLink] Ignore P2P candidate not in layer0");
118 0 : continue;
119 0 : }
120 :
121 : // 构造InterFace对象用于后续生成Link
122 : shared_ptr<NetInstance::ConnInterface> sourceIface = make_shared<NetInstance::ConnInterface>(
123 0 : src2detAddr, src2detPorts, src2detLink->GetSourceIFace()->GetPos(), LinkType::PEER2PEER,
124 0 : src2detLink->GetLinkProtocols(), src2detLink->GetTopoType(), src2detLink->GetTopoInstId());
125 : shared_ptr<NetInstance::ConnInterface> targetIface = make_shared<NetInstance::ConnInterface>(
126 0 : det2dstAddr, det2dstPorts, det2dstLink->GetSourceIFace()->GetPos(), LinkType::PEER2PEER,
127 0 : det2dstLink->GetLinkProtocols(), det2dstLink->GetTopoType(), det2dstLink->GetTopoInstId());
128 :
129 : // 构造Link加入NetInstance中
130 : shared_ptr<NetInstance::Link> sendEdge = make_shared<NetInstance::Link>(
131 0 : data.srcNetInstPeer, data.dstNetInstPeer, sourceIface, targetIface, linkType, newLinkProtocols,
132 0 : LinkDirection::SEND_ONLY, hop);
133 : shared_ptr<NetInstance::Link> recvEdge = make_shared<NetInstance::Link>(
134 0 : data.dstNetInstPeer, data.srcNetInstPeer, targetIface, sourceIface, linkType, newLinkProtocols,
135 0 : LinkDirection::RECV_ONLY, hop);
136 0 : innerNetInst->AddLink(sendEdge);
137 0 : innerNetInst->AddLink(recvEdge);
138 :
139 0 : data.srcNetInstPeer->AddConnInterface(0, sourceIface);
140 0 : data.dstNetInstPeer->AddConnInterface(0, targetIface);
141 :
142 0 : HCCL_DEBUG(
143 : "[DetourService][AddDetourLink] add SEND_ONLY and RECV_ONLY two links: linkType[%s]"
144 : " sourceIfaceAddress[%s] targetIfaceAddress[%s]",
145 : linkType.Describe().c_str(), sourceIface->GetAddr().Describe().c_str(),
146 : targetIface->GetAddr().Describe().c_str());
147 0 : }
148 : }
149 :
150 0 : HCCL_DEBUG(
151 : "[DetourService][AddDetourLink] srcRankId[%llu] dstRankId[%llu] srcLocalId[%llu] detourLocalId[%llu] "
152 : "dstLocalId[%llu] src2detVec.size[%u] det2dstVec.size[%u]",
153 : data.srcNetInstPeer->GetNodeId(), data.dstNetInstPeer->GetNodeId(), data.srcPhyPeerId, data.detourPhyPeerId,
154 : data.dstPhyPeerId, src2detVec.size(), det2dstVec.size());
155 0 : }
156 :
157 24 : std::vector<LocalId> GetInnerLocalIds(const RankGraph* rankGraph)
158 : {
159 24 : if (rankGraph == nullptr) {
160 0 : THROW<NullPtrException>(StringFormat("[GetInnerLocalIds] rankGraph is nullptr"));
161 : }
162 24 : const NetInstance* innerNetInst = rankGraph->GetNetInstanceByRankId(0, rankGraph->GetMyRank());
163 24 : if (innerNetInst == nullptr) {
164 0 : THROW<NullPtrException>(StringFormat("[GetInnerLocalIds] innerNetInst is nullptr"));
165 : }
166 24 : set<RankId> innerRanks = innerNetInst->GetRankIds();
167 24 : std::vector<LocalId> localIds;
168 85 : for (auto& rankId : innerRanks) {
169 61 : localIds.emplace_back(rankGraph->GetLocalId(rankId));
170 : }
171 24 : return localIds;
172 24 : }
173 :
174 0 : bool IsInSameRow(const std::vector<LocalId>& localIds)
175 : {
176 0 : int preRowId = -1;
177 0 : for (auto& localId : localIds) {
178 0 : int curRowId = localId / DETOUR_NODE_NUM;
179 0 : if (preRowId == -1) {
180 0 : preRowId = curRowId;
181 : } else {
182 0 : if (curRowId != preRowId) {
183 0 : return false;
184 : }
185 : }
186 : }
187 0 : return true;
188 : }
189 :
190 0 : bool IsInSameCol(const std::vector<LocalId>& localIds)
191 : {
192 0 : int preColId = -1;
193 0 : for (auto& localId : localIds) {
194 0 : int curColId = localId % DETOUR_NODE_NUM;
195 0 : if (preColId == -1) {
196 0 : preColId = curColId;
197 : } else {
198 0 : if (curColId != preColId) {
199 0 : return false;
200 : }
201 : }
202 : }
203 0 : return true;
204 : }
205 :
206 0 : bool GetTableIds(
207 : const std::vector<LocalId>& localIds, std::unordered_map<LocalId, u32>& tableIds, std::set<u32>& tableIdSet)
208 : {
209 0 : if (IsInSameRow(localIds)) {
210 0 : for (auto& localId : localIds) {
211 0 : u32 tableId = localId % DETOUR_NODE_NUM;
212 0 : tableIds[localId] = tableId;
213 0 : tableIdSet.emplace(tableId);
214 : }
215 0 : } else if (IsInSameCol(localIds)) {
216 0 : for (auto& localId : localIds) {
217 0 : u32 tableId = localId / DETOUR_NODE_NUM;
218 0 : tableIds[localId] = tableId;
219 0 : tableIdSet.emplace(tableId);
220 : }
221 : } else {
222 0 : HCCL_WARNING("[DetourService][GetTableIds] The ranks localIds are not in the same row or column.");
223 0 : return false;
224 : }
225 0 : return true;
226 : }
227 :
228 4 : void SetDetourTable4P(
229 : const std::set<u32>& tableIdSet, unordered_map<LocalId, unordered_map<LocalId, vector<LocalId>>>& detourTable)
230 : {
231 12 : if (tableIdSet == std::set<u32>{0, 1, 2, 3}) { // tableId必须为 0 1 2 3,才能使用DETOUR4PTABLE_0123绕路
232 1 : detourTable = GetDetour4PTable0123();
233 1 : HCCL_DEBUG("[DetourService] selected detour table is DETOUR4PTABLE_0123");
234 9 : } else if (tableIdSet == std::set<u32>{4, 5, 6, 7}) { // tableId必须为 4 5 6 7,才能使用DETOUR4PTABLE_4567绕路
235 1 : detourTable = GetDetour4PTable4567();
236 1 : HCCL_DEBUG("[DetourService] selected detour table is DETOUR4PTABLE_4567");
237 6 : } else if (tableIdSet == std::set<u32>{0, 2, 4, 6}) { // tableId必须为 0 2 4 6,才能使用DETOUR4PTABLE_0246绕路
238 1 : detourTable = GetDetour4PTable0246();
239 1 : HCCL_DEBUG("[DetourService] selected detour table is DETOUR4PTABLE_0246");
240 3 : } else if (tableIdSet == std::set<u32>{1, 3, 5, 7}) { // tableId必须为 1 3 5 7,才能使用DETOUR4PTABLE_1357绕路
241 1 : detourTable = GetDetour4PTable1357();
242 1 : HCCL_DEBUG("[DetourService] selected detour table is DETOUR4PTABLE_1357");
243 : } else {
244 0 : HCCL_WARNING("no matched detourTable found");
245 : }
246 4 : }
247 :
248 2 : void SetDetourTable2P(
249 : const std::set<u32>& tableIdSet, unordered_map<LocalId, unordered_map<LocalId, vector<LocalId>>>& detourTable)
250 : {
251 11 : if (tableIdSet == std::set<u32>{0, 1} || tableIdSet == std::set<u32>{2, 3} || tableIdSet == std::set<u32>{4, 5}
252 7 : || tableIdSet == std::set<u32>{6, 7}) {
253 1 : detourTable = GetDetour2PTable01();
254 1 : } else if (
255 3 : tableIdSet == std::set<u32>{0, 4} || tableIdSet == std::set<u32>{1, 5} || tableIdSet == std::set<u32>{2, 6}
256 2 : || tableIdSet == std::set<u32>{3, 7}) {
257 1 : detourTable = GetDetour2PTable04();
258 : } else {
259 0 : HCCL_WARNING("[DetourService][%s]no matched detourTable found", __func__);
260 : }
261 2 : }
262 :
263 24 : void GetDetourTableAndTableIds(
264 : const std::vector<LocalId>& localIds,
265 : std::unordered_map<LocalId, unordered_map<LocalId, vector<LocalId>>>& detourTable,
266 : std::unordered_map<LocalId, u32>& tableIds, const RankTableInfo* rankTable)
267 : {
268 24 : std::set<u32> tableIdSet;
269 24 : HcclDetourType detourType = EnvConfig::GetInstance().GetDetourConfig().GetDetourType();
270 24 : if (rankTable->detour == false) {
271 9 : detourType = HcclDetourType::HCCL_DETOUR_DISABLE;
272 : }
273 24 : switch (detourType) {
274 0 : case HcclDetourType::HCCL_DETOUR_ENABLE_2P: {
275 0 : if (localIds.size() != DETOUR_NODE_NUM_2P) {
276 0 : return;
277 : }
278 0 : bool res = GetTableIds(localIds, tableIds, tableIdSet);
279 0 : if (res) {
280 0 : SetDetourTable2P(tableIdSet, detourTable);
281 0 : HCCL_DEBUG("[DetourService] selected detour type is DETOUR2PTABLE");
282 : } else {
283 0 : HCCL_WARNING("[DetourService] detourtype [%s] does not support.", detourType.Describe().c_str());
284 : }
285 0 : break;
286 : }
287 0 : case HcclDetourType::HCCL_DETOUR_ENABLE_4P: {
288 0 : if (localIds.size() != DETOUR_NODE_NUM_4P) {
289 0 : return;
290 : }
291 0 : bool res = GetTableIds(localIds, tableIds, tableIdSet);
292 0 : if (res) {
293 0 : SetDetourTable4P(tableIdSet, detourTable);
294 : } else {
295 0 : HCCL_WARNING("[DetourService] detourtype [%s] does not support.", detourType.Describe().c_str());
296 : }
297 0 : break;
298 : }
299 24 : case HcclDetourType::HCCL_DETOUR_DISABLE: {
300 54 : HCCL_DEBUG("[DetourService] detour is disable");
301 24 : break;
302 : }
303 0 : default: {
304 0 : THROW<NotSupportException>(
305 0 : StringFormat("[DetourService] detourtype [%s] does not support.", detourType.Describe().c_str()));
306 : }
307 : }
308 24 : }
309 :
310 0 : void AddDetourLinks(
311 : const PhyTopo* phyTopo, RankGraph* rankGraph,
312 : std::unordered_map<LocalId, unordered_map<LocalId, vector<LocalId>>>& detourTable,
313 : std::unordered_map<LocalId, u32>& tableIds, const RankTableInfo* rankTable)
314 : {
315 0 : auto phyTopoGraph = phyTopo->GetTopoGraph();
316 0 : NetInstance* innerNetInst = rankGraph->GetNetInstanceByRankId(0, rankGraph->GetMyRank());
317 0 : if (innerNetInst == nullptr) {
318 0 : THROW<NullPtrException>(StringFormat("[DetourService] innerNetInst is nullptr"));
319 : }
320 0 : set<RankId> innerRanks = innerNetInst->GetRankIds();
321 0 : for (const auto& srcRankId : innerRanks) {
322 0 : LocalId srcLocalId = rankGraph->GetLocalId(srcRankId);
323 0 : u32 srcTableId = tableIds[srcLocalId];
324 0 : for (const auto& dstRankId : innerRanks) {
325 0 : LocalId dstLocalId = rankGraph->GetLocalId(dstRankId);
326 0 : u32 dstTableId = tableIds[dstLocalId];
327 :
328 0 : if (detourTable.count(srcTableId) == 0 || detourTable[srcTableId].count(dstTableId) == 0) {
329 0 : continue;
330 : }
331 :
332 0 : auto detourTableIds = detourTable[srcTableId][dstTableId];
333 0 : for (auto& detourTableId : detourTableIds) {
334 0 : LocalId detourLocalId = detourTableId + srcLocalId - srcTableId;
335 :
336 : // 插入detourlink
337 0 : DetourData detourData;
338 0 : detourData.detourPhyPeerId = PhyTopo::Peer::GetId(detourLocalId); // 绕路可能没有绕路节点的rankid
339 0 : detourData.srcPhyPeerId = PhyTopo::Peer::GetId(srcLocalId);
340 0 : detourData.dstPhyPeerId = PhyTopo::Peer::GetId(dstLocalId);
341 0 : detourData.srcNetInstPeer = rankGraph->GetPeer(srcRankId);
342 0 : detourData.dstNetInstPeer = rankGraph->GetPeer(dstRankId);
343 :
344 0 : AddDetourLink(innerNetInst, detourData, phyTopoGraph, rankTable);
345 0 : }
346 0 : }
347 : }
348 0 : }
349 :
350 24 : void DetourService::InsertDetourLinks(RankGraph* rankGraph, const RankTableInfo* rankTable)
351 : {
352 24 : std::unordered_map<LocalId, u32> tableIds;
353 24 : std::unordered_map<LocalId, unordered_map<LocalId, vector<LocalId>>> detourTable;
354 :
355 : // 获取innerGroup的localIds
356 24 : std::vector<LocalId> localIds = GetInnerLocalIds(rankGraph);
357 : // 获取当前localIds的tableId和detourTable
358 24 : GetDetourTableAndTableIds(localIds, detourTable, tableIds, rankTable);
359 :
360 24 : if (!detourTable.empty()) {
361 : // 添加绕路links
362 0 : AddDetourLinks(phyTopo, rankGraph, detourTable, tableIds, rankTable);
363 : } else {
364 54 : HCCL_WARNING("no detour links found");
365 : }
366 24 : }
367 :
368 : } // namespace Hccl
|