Line data Source code
1 : /**
2 : * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3 : * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4 : * CANN Open Software License Agreement Version 2.0 (the "License").
5 : * Please refer to the License for details. You may not use this file except in compliance with the License.
6 : * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7 : * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8 : * See LICENSE in the root of the software repository for the full text of the License.
9 : */
10 :
11 : #include "hccl_team_c_adpt.h"
12 :
13 : #include <sstream>
14 : #include <string>
15 : #include <unordered_map>
16 : #include <utility>
17 : #include <vector>
18 :
19 : #include "adapter_rts_common.h"
20 : #include "hccl_comm_pub.h"
21 : #include "hccl_team.h"
22 : #include "hccl_team_mgr.h"
23 : #include "hccl/hccl_rank_graph.h"
24 : #include "hccl/hccl_res.h"
25 : #include "hcomm_result_defs.h"
26 : #include "hcomm_team.h"
27 : #include "hcomm_team_c_adpt.h"
28 : #include "hcomm_team_mgr.h"
29 :
30 : using namespace hccl;
31 :
32 : /**
33 : * @note 职责:集合通信的通信域HcclTeam管理的C接口的C到C++适配
34 : */
35 :
36 : /* 在 rankIds 中查找 selfRankId 的下标作为 memberId。
37 : * 例如 rankIds=[1,3,5,7],selfRankId=3,则 selfMemberId=1。未找到返回 HCCL_E_NOT_FOUND。 */
38 43 : static HcclResult FindSelfMemberId(const HcclTeamCreateDesc* desc, uint32_t& selfMemberId)
39 : {
40 88 : for (uint32_t i = 0; i < desc->rankNum; i++) {
41 86 : if (desc->rankIds[i] == desc->selfRankId) {
42 41 : selfMemberId = i;
43 41 : return HCCL_SUCCESS;
44 : }
45 : }
46 2 : HCCL_ERROR("[FindSelfMemberId] selfRankId[%u] not found in rankIds", desc->selfRankId);
47 2 : return HCCL_E_NOT_FOUND;
48 : }
49 :
50 : /* 从 HcclTeamCreateDesc 填充 HcommTeamCreateDesc。
51 : * worldMemberIds:worldTeam 传 nullptr(L3 生成 [0,memberNum));subTeam 传 worldTeam 的 memberId 列表。 */
52 40 : static void FillHcommTeamCreateDesc(
53 : const HcclTeamCreateDesc* desc, uint32_t selfMemberId, const uint32_t* worldMemberIds,
54 : HcommTeamCreateDesc& hcommDesc)
55 : {
56 : /* 用头文件 Init 初始化 ABI 头部(magicWord/version/size)与默认字段,再覆盖业务字段。 */
57 40 : (void)HcommTeamCreateDescInit(&hcommDesc);
58 40 : hcommDesc.memberNum = desc->rankNum;
59 40 : hcommDesc.selfMemberId = selfMemberId;
60 40 : hcommDesc.worldMemberIds = worldMemberIds;
61 40 : hcommDesc.requirement.signalCount = 0; // 不支持配置,强制为0
62 40 : hcommDesc.requirement.counterCount = 0; // 不支持配置,强制为0
63 40 : hcommDesc.requirement.barrierCount = desc->requirement.barrierCount;
64 40 : hcommDesc.netLayer = desc->netLayer;
65 40 : hcommDesc.protocol = desc->protocol;
66 40 : }
67 :
68 : /* 反查 subTeam.rankIds 在 worldTeam.rankIds 中的下标作为 worldMemberIds(worldTeam 的 memberId)。
69 : * 用 unordered_map 预建 rankId→worldMemberId 反查表,O(N+M) 避免双重循环。 */
70 7 : static HcclResult BuildSubTeamWorldMemberIds(
71 : HcommTeamHandle worldTeam, const uint32_t* subRankIds, uint32_t subRankNum, std::vector<uint32_t>& worldMemberIds)
72 : {
73 7 : std::vector<uint32_t> worldRankIds = HcclTeamMgr::GetInstance().GetRankIds(worldTeam);
74 7 : CHK_PRT_RET(
75 : worldRankIds.empty(), HCCL_ERROR("[%s] GetRankIds failed, worldTeam[%p] not registered", __func__, worldTeam),
76 : HCCL_E_PARA);
77 6 : std::unordered_map<uint32_t, uint32_t> rankToMember;
78 6 : rankToMember.reserve(worldRankIds.size());
79 18 : for (uint32_t m = 0; m < worldRankIds.size(); m++) {
80 12 : rankToMember[worldRankIds[m]] = m;
81 : }
82 6 : worldMemberIds.resize(subRankNum);
83 18 : for (uint32_t i = 0; i < subRankNum; i++) {
84 12 : auto it = rankToMember.find(subRankIds[i]);
85 12 : CHK_PRT_RET(
86 : it == rankToMember.end(),
87 : HCCL_ERROR("[%s] subTeam rankId[%u] not in worldTeam rankIds", __func__, subRankIds[i]), HCCL_E_PARA);
88 12 : worldMemberIds[i] = it->second;
89 : }
90 6 : return HCCL_SUCCESS;
91 7 : }
92 :
93 : /* 申请 syncMem 本地内存。失败时回滚已创建的 team(HcommTeamDestroy)。 */
94 35 : static HcclResult AllocTeamSyncMem(HcommTeamHandle* team, uint64_t syncMemSize, void*& syncMemPtr)
95 : {
96 35 : syncMemPtr = nullptr;
97 35 : HcclResult mallocRet = hrtMalloc(&syncMemPtr, syncMemSize);
98 35 : if (mallocRet != HCCL_SUCCESS || syncMemPtr == nullptr) {
99 2 : HCCL_ERROR("[AllocTeamSyncMem] hrtMalloc failed, ret[%d] size[%llu]", mallocRet, syncMemSize);
100 2 : (void)HcommTeamDestroy(*team);
101 2 : *team = nullptr;
102 2 : return HCCL_E_MEMORY;
103 : }
104 33 : return HCCL_SUCCESS;
105 : }
106 :
107 43 : HcclResult HcclWorldTeamCreate(HcclComm comm, const HcclTeamCreateDesc* desc, HcommTeamHandle* worldTeam)
108 : {
109 43 : CHK_PTR_NULL(comm);
110 41 : CHK_PTR_NULL(desc);
111 40 : CHK_PTR_NULL(worldTeam);
112 39 : CHK_PRT_RET(
113 : (desc->rankNum == 0 || desc->rankNum == 1),
114 : HCCL_ERROR("[%s] world team can not be empty or single rank", __func__), HCCL_E_PARA);
115 37 : CHK_PRT_RET(desc->rankIds == nullptr, HCCL_ERROR("[%s] rankIds is null", __func__), HCCL_E_PTR);
116 36 : CHK_PRT_RET(
117 : (desc->requirement.signalCount != 0 || desc->requirement.counterCount != 0),
118 : HCCL_ERROR(
119 : "[%s] signalCount[%u] and counterCount[%u] must be 0", __func__, desc->requirement.signalCount,
120 : desc->requirement.counterCount),
121 : HCCL_E_PARA);
122 36 : CHK_PRT_RET(
123 : desc->requirement.barrierCount == 0, HCCL_ERROR("[%s] barrierCount must be >= 1", __func__), HCCL_E_PARA);
124 :
125 36 : auto* hcclComm = static_cast<hccl::hcclComm*>(comm);
126 36 : CollComm* collComm = hcclComm->GetCollComm();
127 36 : CHK_PTR_NULL(collComm);
128 36 : const std::string commId = collComm->GetCommId();
129 :
130 : /* 0. 校验 desc 与通信域匹配(worldTeam 可为子集)。 */
131 36 : uint32_t commRankSize = collComm->GetRankSize();
132 36 : CHK_PRT_RET(
133 : desc->rankNum > commRankSize,
134 : HCCL_ERROR(
135 : "[%s] rankNum[%u] > commRankSize[%u], comm[%s]", __func__, desc->rankNum, commRankSize, commId.c_str()),
136 : HCCL_E_PARA);
137 :
138 : /* 1. selfRankId 是本 rank 的实际 rankId,需在 rankIds 中查找其下标作为 memberId。 */
139 35 : uint32_t selfMemberId = 0;
140 35 : CHK_RET(FindSelfMemberId(desc, selfMemberId));
141 :
142 : /* 2. 填充 HcommTeamCreateDesc,world team 无父 team,worldMemberIds 传 nullptr(L3 生成 [0,memberNum))。 */
143 34 : HcommTeamCreateDesc hcommDesc = {};
144 34 : FillHcommTeamCreateDesc(desc, selfMemberId, nullptr, hcommDesc);
145 :
146 : /* 3. 调用 Hcomm 层创建 world team,outSyncMemSize 为需要本地申请的 syncMem 字节数。 */
147 34 : uint64_t syncMemSize = 0;
148 34 : HcommResult ret = HcommTeamCreate(nullptr, &hcommDesc, worldTeam, &syncMemSize);
149 34 : CHK_PRT_RET(
150 : ret != 0 || *worldTeam == nullptr,
151 : HCCL_ERROR(
152 : "[%s] HcommTeamCreate failed, comm[%s] ret[%d] rankNum[%u] selfRankId[%u]", __func__, commId.c_str(), ret,
153 : desc->rankNum, desc->selfRankId),
154 : (ret != 0) ? static_cast<HcclResult>(ret) : HCCL_E_INTERNAL);
155 32 : CHK_PRT_RET(syncMemSize == 0, HCCL_ERROR("[%s] syncMemSize is 0", __func__), HCCL_E_PARA);
156 :
157 : /* 4. 申请 syncMem 本地内存,后续基于 channel 交换。失败时回滚已创建的 team。 */
158 31 : void* syncMemPtr = nullptr;
159 31 : CHK_RET(AllocTeamSyncMem(worldTeam, syncMemSize, syncMemPtr));
160 :
161 : /* 5. 注册 worldTeam 到 HcclTeamMgr(存 syncMem + collComm 反查 + rankIds),供 HcclSubTeamCreate/HcclTeamDestroy
162 : * 反查。 */
163 30 : HcclResult regRet = HcclTeamMgr::GetInstance().RegisterWorldTeam(
164 30 : *worldTeam, collComm, syncMemPtr, syncMemSize, desc->rankIds, desc->rankNum);
165 30 : if (regRet != HCCL_SUCCESS) {
166 0 : HCCL_ERROR("[%s] RegisterWorldTeam failed, comm[%s] ret[%d]", __func__, commId.c_str(), regRet);
167 0 : (void)hrtFree(syncMemPtr);
168 0 : (void)HcommTeamDestroy(*worldTeam);
169 0 : *worldTeam = nullptr;
170 0 : return regRet;
171 : }
172 :
173 30 : HCCL_INFO(
174 : "[%s] success, team[%p] comm[%s] rankNum[%u] selfRankId[%u] selfMemberId[%u] syncMemSize[%llu]", __func__,
175 : *worldTeam, commId.c_str(), desc->rankNum, desc->selfRankId, selfMemberId, syncMemSize);
176 30 : return HCCL_SUCCESS;
177 36 : }
178 :
179 13 : HcclResult HcclSubTeamCreate(HcommTeamHandle worldTeam, const HcclTeamCreateDesc* desc, HcommTeamHandle* team)
180 : {
181 13 : CHK_PTR_NULL(worldTeam);
182 12 : CHK_PTR_NULL(desc);
183 11 : CHK_PTR_NULL(team);
184 :
185 10 : CHK_PRT_RET(
186 : (desc->rankNum == 0 || desc->rankNum == 1),
187 : HCCL_ERROR("[%s] sub team can not be empty or single rank", __func__), HCCL_E_PARA);
188 9 : CHK_PRT_RET(desc->rankIds == nullptr, HCCL_ERROR("[%s] rankIds is null", __func__), HCCL_E_PTR);
189 8 : CHK_PRT_RET(
190 : (desc->requirement.signalCount != 0 || desc->requirement.counterCount != 0),
191 : HCCL_ERROR(
192 : "[%s] signalCount[%u] and counterCount[%u] must be 0", __func__, desc->requirement.signalCount,
193 : desc->requirement.counterCount),
194 : HCCL_E_PARA);
195 8 : CHK_PRT_RET(
196 : desc->requirement.barrierCount == 0, HCCL_ERROR("[%s] barrierCount must be >= 1", __func__), HCCL_E_PARA);
197 :
198 : /* selfRankId 是本 rank 的实际 rankId,需在 rankIds 中查找其下标作为 memberId。 */
199 8 : uint32_t selfMemberId = 0;
200 8 : CHK_RET(FindSelfMemberId(desc, selfMemberId));
201 :
202 : /* 1. 反查 subTeam 的 rankIds 在 worldTeam.rankIds 中的下标作为 worldMemberIds(worldTeam 的 memberId)。 */
203 7 : std::vector<uint32_t> worldMemberIds;
204 7 : CHK_RET(BuildSubTeamWorldMemberIds(worldTeam, desc->rankIds, desc->rankNum, worldMemberIds));
205 :
206 : /* 2. 填充 HcommTeamCreateDesc:成员为 desc 的子集,父 team 为 worldTeam,worldMemberIds 用反查结果。 */
207 6 : HcommTeamCreateDesc hcommDesc = {};
208 6 : FillHcommTeamCreateDesc(desc, selfMemberId, worldMemberIds.data(), hcommDesc);
209 :
210 : /* 3. 调用 Hcomm 层创建 sub team,outSyncMemSize 为需要本地申请的 syncMem 字节数。 */
211 6 : uint64_t syncMemSize = 0;
212 6 : HcommResult ret = HcommTeamCreate(worldTeam, &hcommDesc, team, &syncMemSize);
213 6 : CHK_PRT_RET(
214 : ret != 0 || *team == nullptr,
215 : HCCL_ERROR(
216 : "[%s] HcommTeamCreate failed, ret[%d] rankNum[%u] selfRankId[%u]", __func__, ret, desc->rankNum,
217 : desc->selfRankId),
218 : (ret != 0) ? static_cast<HcclResult>(ret) : HCCL_E_INTERNAL);
219 5 : CHK_PRT_RET(syncMemSize == 0, HCCL_ERROR("[%s] syncMemSize is 0", __func__), HCCL_E_PARA);
220 :
221 : /* 4. 申请 syncMem 本地内存。失败时回滚已创建的 sub team。 */
222 4 : void* syncMemPtr = nullptr;
223 4 : CHK_RET(AllocTeamSyncMem(team, syncMemSize, syncMemPtr));
224 :
225 : /* 5. 注册 sub team 到 HcclTeamMgr(建父子关系 + 存 syncMem + rankIds,collComm 取自 worldTeam 条目)。 */
226 3 : HcclResult regRet = HcclTeamMgr::GetInstance().RegisterSubTeam(
227 3 : worldTeam, *team, syncMemPtr, syncMemSize, desc->rankIds, desc->rankNum);
228 3 : if (regRet != HCCL_SUCCESS) {
229 0 : HCCL_ERROR("[%s] RegisterSubTeam failed, ret[%d]", __func__, regRet);
230 0 : (void)hrtFree(syncMemPtr);
231 0 : (void)HcommTeamDestroy(*team);
232 0 : *team = nullptr;
233 0 : return regRet;
234 : }
235 :
236 3 : HCCL_INFO(
237 : "[%s] success, team[%p] worldTeam[%p] rankNum[%u] selfRankId[%u] selfMemberId[%u] syncMemSize[%llu]", __func__,
238 : *team, worldTeam, desc->rankNum, desc->selfRankId, selfMemberId, syncMemSize);
239 3 : return HCCL_SUCCESS;
240 7 : }
241 :
242 34 : HcclResult HcclTeamDestroy(HcommTeamHandle team)
243 : {
244 34 : CHK_PTR_NULL(team);
245 :
246 : /* worldTeam 销毁时连带销毁其所有 subTeam 与 window(1:N),避免泄漏。subTeam 无子 team、无 window。 */
247 33 : if (HcclTeamMgr::GetInstance().FindWorldTeam(team) == team) {
248 : // 先销毁所有 subTeam(递归调 HcclTeamDestroy,subTeam 不会重复进入此分支)
249 29 : std::vector<HcommTeamHandle> subTeams = HcclTeamMgr::GetInstance().GetSubTeams(team);
250 30 : for (HcommTeamHandle sub : subTeams) {
251 1 : (void)HcclTeamDestroy(sub);
252 : }
253 : // 再销毁 worldTeam 拥有的 window
254 29 : std::vector<WindowInfo> windows = HcclTeamMgr::GetInstance().GetWorldTeamWindows(team);
255 48 : for (const auto& win : windows) {
256 19 : if (win.handle != nullptr) {
257 19 : (void)HcommTeamWindowDeregister(team, win.handle);
258 : }
259 : }
260 29 : }
261 :
262 : /* 释放该 team 的 syncMem(hrtFree)+ erase 条目。 */
263 33 : HcclTeamMgr::GetInstance().UnregisterTeam(team);
264 :
265 33 : HcommResult ret = HcommTeamDestroy(team);
266 33 : CHK_PRT_RET(
267 : ret != 0, HCCL_ERROR("[%s] HcommTeamDestroy failed, ret[%d]", __func__, ret), static_cast<HcclResult>(ret));
268 :
269 32 : HCCL_INFO("[%s] success", __func__);
270 32 : return HCCL_SUCCESS;
271 : }
272 :
273 : /* 注册 team 的 syncMem 内存(team 粒度,仅首次注册一次)。
274 : * syncMemHandle 已存在则跳过注册;syncMemHandle 出参返回当前句柄(首次或已存在),供调用方日志使用。 */
275 : static HcclResult
276 11 : RegisterTeamSyncMem(HcommTeamHandle team, const std::string& commId, CommMems* commMem, HcclMemHandle& syncMemHandle)
277 : {
278 11 : syncMemHandle = HcclTeamMgr::GetInstance().GetTeamSyncMemHandle(team);
279 11 : if (syncMemHandle != nullptr) {
280 0 : HCCL_INFO(
281 : "[RegisterTeamSyncMem] syncMemHandle[%p] already registered, comm[%s] team[%p]", syncMemHandle,
282 : commId.c_str(), team);
283 0 : return HCCL_SUCCESS;
284 : }
285 11 : void* syncMemPtr = HcclTeamMgr::GetInstance().GetSyncMemPtr(team);
286 11 : uint64_t syncMemSize = HcclTeamMgr::GetInstance().GetSyncMemSize(team);
287 11 : if (syncMemPtr == nullptr || syncMemSize == 0) {
288 0 : HCCL_ERROR(
289 : "[RegisterTeamSyncMem] syncMemPtr[%p] or syncMemSize[%llu] is invalid, comm[%s] team[%p]", syncMemPtr,
290 : syncMemSize, commId.c_str(), team);
291 0 : return HCCL_E_INTERNAL;
292 : }
293 11 : CommMem syncMemVar{};
294 11 : syncMemVar.type = COMM_MEM_TYPE_DEVICE;
295 11 : syncMemVar.addr = syncMemPtr;
296 11 : syncMemVar.size = syncMemSize;
297 11 : std::ostringstream syncMemTagStream;
298 11 : syncMemTagStream << HCCL_TEAM_SYNCMEM_TAG_PREFIX << commId << "_team_" << team << "_addr_" << syncMemPtr << "_size_"
299 11 : << syncMemSize;
300 11 : const std::string syncMemTagStr = syncMemTagStream.str();
301 11 : HcclResult ret = commMem->CommRegMem(syncMemTagStr, syncMemVar, &syncMemHandle);
302 11 : CHK_PRT_RET(
303 : ret != HCCL_SUCCESS,
304 : HCCL_ERROR(
305 : "[RegisterTeamSyncMem] register syncMem failed, comm[%s] ret[%d] size[%llu]", commId.c_str(), ret,
306 : syncMemSize),
307 : ret);
308 11 : HcclTeamMgr::GetInstance().SetTeamSyncMemHandle(team, syncMemHandle, syncMemTagStr);
309 11 : return HCCL_SUCCESS;
310 11 : }
311 :
312 : /* 重复注册检测:worldTeam 已注册过 window 且本次 localMem 是某 window 的 registeredLocalMem 的子集,则复用。
313 : * 命中复用时设 *window 并返回 true;否则返回 false。 */
314 18 : static bool TryReuseWindow(
315 : HcommTeamHandle worldTeam, const CommMem& localMem, const std::string& commId, HcommTeamHandle team,
316 : HcommWindowHandle* window)
317 : {
318 18 : HcommWindowHandle reusableWindow = nullptr;
319 18 : if (!HcclTeamMgr::GetInstance().FindReusableWindow(worldTeam, localMem, reusableWindow)) {
320 15 : return false;
321 : }
322 3 : *window = reusableWindow;
323 3 : HCCL_INFO(
324 : "[TryReuseWindow] reuse registered window, comm[%s] team[%p] worldTeam[%p] window[%p]", commId.c_str(), team,
325 : worldTeam, *window);
326 3 : return true;
327 : }
328 :
329 : /* 非复用路径:在 worldTeam 上创建业务 window,注册 localMem(失败回滚 window),记录到 worldTeam 的 window 列表。 */
330 15 : static HcclResult CreateNewWindow(
331 : HcommTeamHandle worldTeam, const CommMem& localMem, const std::string& commId, CommMems* commMem,
332 : HcommWindowHandle* window)
333 : {
334 15 : HcommResult hRet = HcommTeamWindowRegister(worldTeam, nullptr, window, HCOMM_TEAM_WINDOW_FLAG_SYMMETRIC);
335 15 : if (hRet != 0 || *window == nullptr) {
336 1 : HCCL_ERROR(
337 : "[CreateNewWindow] HcommTeamWindowRegister failed, comm[%s] worldTeam[%p] ret[%d]", commId.c_str(),
338 : worldTeam, hRet);
339 1 : *window = nullptr;
340 1 : return (hRet != 0) ? static_cast<HcclResult>(hRet) : HCCL_E_INTERNAL;
341 : }
342 14 : std::ostringstream userTagStream;
343 14 : userTagStream << HCCL_TEAM_USERMEM_TAG_PREFIX << commId << "_addr_" << localMem.addr << "_size_" << localMem.size;
344 14 : const std::string userTag = userTagStream.str();
345 14 : HcclMemHandle userHandle = nullptr;
346 14 : HcclResult ret = commMem->CommRegMem(userTag, localMem, &userHandle);
347 14 : if (ret != HCCL_SUCCESS) {
348 0 : HCCL_ERROR("[CreateNewWindow] register localMem failed, comm[%s] ret[%d]", commId.c_str(), ret);
349 0 : (void)HcommTeamWindowDeregister(worldTeam, *window);
350 0 : *window = nullptr;
351 0 : return ret;
352 : }
353 14 : HcclTeamMgr::GetInstance().AddWorldTeamWindow(worldTeam, *window, localMem, userHandle, userTag);
354 14 : return HCCL_SUCCESS;
355 14 : }
356 :
357 26 : HcclResult HcclTeamWindowRegister(
358 : HcclComm comm, HcommTeamHandle worldTeam, const CommMem* localMem, HcommWindowHandle* window, uint32_t flag)
359 : {
360 26 : CHK_PTR_NULL(comm);
361 24 : CHK_PTR_NULL(worldTeam);
362 23 : CHK_PTR_NULL(localMem);
363 23 : CHK_PTR_NULL(window);
364 22 : CHK_PRT_RET(flag != 0, HCCL_ERROR("[%s] flag[%u] is not supported, only support 0", __func__, flag), HCCL_E_PARA);
365 22 : CHK_PRT_RET(
366 : (localMem->type != COMM_MEM_TYPE_DEVICE),
367 : HCCL_ERROR("[%s] localMem type[%d] must be device", __func__, localMem->type), HCCL_E_PARA);
368 22 : CHK_PRT_RET(localMem->addr == nullptr, HCCL_ERROR("[%s] localMem addr is null", __func__), HCCL_E_PTR);
369 21 : CHK_PRT_RET(localMem->size == 0, HCCL_ERROR("[%s] localMem size is 0", __func__), HCCL_E_PARA);
370 :
371 20 : auto* hcclComm = static_cast<hccl::hcclComm*>(comm);
372 20 : CollComm* collComm = hcclComm->GetCollComm();
373 20 : CHK_PTR_NULL(collComm);
374 20 : const std::string commId = collComm->GetCommId();
375 :
376 : /* 入参 worldTeam 必须是 worldTeam(syncMem 注册已移至 ChannelsCreate,window 归 worldTeam 所有)。 */
377 20 : CHK_PRT_RET(
378 : HcclTeamMgr::GetInstance().FindWorldTeam(worldTeam) != worldTeam,
379 : HCCL_ERROR("[%s] worldTeam[%p] is not worldTeam", __func__, worldTeam), HCCL_E_PARA);
380 18 : CollComm* worldCollComm = HcclTeamMgr::GetInstance().FindCollComm(worldTeam);
381 18 : CHK_PTR_NULL(worldCollComm);
382 18 : CHK_PRT_RET(
383 : worldCollComm->GetCommId() != commId,
384 : HCCL_ERROR("[%s] worldTeam[%p] is not belong to comm[%s]", __func__, worldTeam, commId.c_str()), HCCL_E_PARA);
385 :
386 18 : auto myRank = collComm->GetMyRank();
387 18 : CHK_PTR_NULL(myRank);
388 18 : CommMems* commMem = myRank->GetCommMems();
389 18 : CHK_PTR_NULL(commMem);
390 :
391 18 : if (TryReuseWindow(worldTeam, *localMem, commId, worldTeam, window)) {
392 3 : return HCCL_SUCCESS;
393 : }
394 :
395 15 : CHK_RET(CreateNewWindow(worldTeam, *localMem, commId, commMem, window));
396 :
397 14 : HCCL_INFO("[%s] success, comm[%s] worldTeam[%p] window[%p]", __func__, commId.c_str(), worldTeam, *window);
398 14 : return HCCL_SUCCESS;
399 20 : }
400 :
401 6 : HcclResult HcclTeamWindowDeregister(HcommTeamHandle team, HcommWindowHandle window)
402 : {
403 6 : CHK_PTR_NULL(team);
404 5 : CHK_PTR_NULL(window);
405 :
406 : /* 1. 取入参 team 对应的 worldTeam。 */
407 4 : HcommTeamHandle worldTeam = HcclTeamMgr::GetInstance().FindWorldTeam(team);
408 4 : CHK_PRT_RET(
409 : worldTeam == nullptr,
410 : HCCL_WARNING("[%s] FindWorldTeam failed, team[%p] not registered, maybe already destroyed", __func__, team),
411 : HCCL_SUCCESS);
412 :
413 : /* 2. 从 worldTeam 的 window 列表移除该 window 的记录(WindowInfo)。
414 : * 注:不注销 localMem 的 MemReg(由通信域析构兜底清理);不处理 syncMem(team 粒度,team 销毁时释放)。 */
415 3 : HcclTeamMgr::GetInstance().RemoveWorldTeamWindow(worldTeam, window);
416 :
417 : /* 3. 销毁 Hcomm 层 window。 */
418 3 : HcommResult ret = HcommTeamWindowDeregister(worldTeam, window);
419 3 : CHK_PRT_RET(
420 : ret != 0, HCCL_ERROR("[%s] HcommTeamWindowDeregister failed, ret[%d]", __func__, ret),
421 : static_cast<HcclResult>(ret));
422 :
423 2 : HCCL_INFO("[%s] success, team[%p] worldTeam[%p] window[%p]", __func__, team, worldTeam, window);
424 2 : return HCCL_SUCCESS;
425 : }
426 :
427 : /* 查询本rank到peerRank的link,填充 HcclChannelDesc 的 endpoint/protocol 字段。*/
428 : static HcclResult
429 11 : FillChannelDescForPeer(HcclComm comm, HcommTeamHandle team, uint32_t selfRank, uint32_t peerRank, HcclChannelDesc& desc)
430 : {
431 11 : uint32_t netLayer = 0;
432 11 : HcommResult hRet = HcommTeamGetNetLayer(team, &netLayer);
433 11 : CHK_PRT_RET(
434 : hRet != 0, HCCL_ERROR("[%s] HcommTeamGetNetLayer failed, ret[%d]", __func__, hRet),
435 : (hRet != 0) ? static_cast<HcclResult>(hRet) : HCCL_E_INTERNAL);
436 :
437 10 : CommLink* links = nullptr;
438 10 : uint32_t linkNum = 0;
439 10 : HcclResult ret = HcclRankGraphGetLinks(comm, netLayer, selfRank, peerRank, &links, &linkNum);
440 10 : if (ret != HCCL_SUCCESS || links == nullptr || linkNum == 0) {
441 1 : HCCL_ERROR("[%s] no link found from rank[%u] to rank[%u]", __func__, selfRank, peerRank);
442 1 : return HCCL_E_NOT_FOUND;
443 : }
444 9 : const CommLink& link = links[0];
445 9 : desc.remoteRank = peerRank;
446 9 : desc.channelProtocol = link.linkAttr.linkProtocol;
447 9 : desc.localEndpoint = link.srcEndpointDesc;
448 9 : desc.remoteEndpoint = link.dstEndpointDesc;
449 9 : return HCCL_SUCCESS;
450 : }
451 :
452 : /* 从 HcclTeamMgr 取 team 的 rankIds(memberId→rankId),推算 memberNum 与 selfMemberId。 */
453 11 : static HcclResult GetTeamMemberInfo(HcommTeamHandle team, uint32_t selfRank, ChannelsCreateCtx& ctx)
454 : {
455 11 : ctx.rankIds = HcclTeamMgr::GetInstance().GetRankIds(team);
456 11 : CHK_PRT_RET(
457 : ctx.rankIds.empty(), HCCL_ERROR("[%s] GetRankIds failed, team[%p] not registered", __func__, team),
458 : HCCL_E_PARA);
459 11 : ctx.memberNum = static_cast<uint32_t>(ctx.rankIds.size());
460 11 : bool selfFound = false;
461 22 : for (uint32_t m = 0; m < ctx.memberNum; m++) {
462 22 : if (ctx.rankIds[m] == selfRank) {
463 11 : ctx.selfMemberId = m;
464 11 : selfFound = true;
465 11 : break;
466 : }
467 : }
468 11 : CHK_PRT_RET(!selfFound, HCCL_ERROR("[%s] selfRank[%u] not in team rankIds", __func__, selfRank), HCCL_E_PARA);
469 11 : return HCCL_SUCCESS;
470 : }
471 :
472 : /* 取 worldTeam 及其所有 window、memHandles;取 worldTeam 维度信息并计算 curToWorld 映射。 */
473 11 : static HcclResult GetWorldTeamContext(HcommTeamHandle team, uint32_t selfRank, ChannelsCreateCtx& ctx)
474 : {
475 11 : ctx.worldTeam = HcclTeamMgr::GetInstance().FindWorldTeam(team);
476 11 : CHK_PTR_NULL(ctx.worldTeam);
477 11 : ctx.windows = HcclTeamMgr::GetInstance().GetWorldTeamWindows(ctx.worldTeam); // 移动赋值,避免深拷贝
478 11 : ctx.syncMemTag = HcclTeamMgr::GetInstance().GetTeamSyncMemTag(team);
479 : // 只收集未交换的 memHandles(syncMemHandle + window localMemHandle),避免重复建链交换
480 11 : ctx.memHandles = HcclTeamMgr::GetInstance().CollectPendingMemHandles(ctx.worldTeam, team);
481 :
482 11 : ctx.worldRankIds = HcclTeamMgr::GetInstance().GetRankIds(ctx.worldTeam);
483 11 : CHK_PRT_RET(
484 : ctx.worldRankIds.empty(),
485 : HCCL_ERROR("[%s] GetRankIds failed, worldTeam[%p] not registered", __func__, ctx.worldTeam), HCCL_E_PARA);
486 11 : ctx.worldMemberNum = static_cast<uint32_t>(ctx.worldRankIds.size());
487 11 : bool worldSelfFound = false;
488 22 : for (uint32_t w = 0; w < ctx.worldMemberNum; w++) {
489 22 : if (ctx.worldRankIds[w] == selfRank) {
490 11 : ctx.worldSelfMemberId = w;
491 11 : worldSelfFound = true;
492 11 : break;
493 : }
494 : }
495 11 : CHK_PRT_RET(
496 : !worldSelfFound, HCCL_ERROR("[%s] selfRank[%u] not in worldTeam rankIds", __func__, selfRank), HCCL_E_PARA);
497 : // curToWorld[m] = 当前 team memberId m 的 rankId 在 worldRankIds 中的下标(worldMemberId)
498 11 : ctx.curToWorld.assign(ctx.memberNum, 0);
499 33 : for (uint32_t m = 0; m < ctx.memberNum; m++) {
500 22 : uint32_t rankId = ctx.rankIds[m];
501 33 : for (uint32_t w = 0; w < ctx.worldMemberNum; w++) {
502 33 : if (ctx.worldRankIds[w] == rankId) {
503 22 : ctx.curToWorld[m] = w;
504 22 : break;
505 : }
506 : }
507 : }
508 11 : return HCCL_SUCCESS;
509 : }
510 :
511 : /* 对每个 peer member 创建 channelCnt 个 channel,结果存 ctx.channelsByMember(self 为空)。 */
512 11 : static HcclResult AcquireChannels(
513 : HcclComm comm, HcommTeamHandle team, const HcclTeamCreateChannelsDesc* desc, uint32_t selfRank,
514 : ChannelsCreateCtx& ctx)
515 : {
516 11 : uint32_t channelCnt = desc->channelCnt;
517 11 : ctx.channelsByMember.assign(ctx.memberNum, {});
518 27 : for (uint32_t m = 0; m < ctx.memberNum; m++) {
519 19 : if (m == ctx.selfMemberId) {
520 8 : continue; // 本 member 不建到自己的 channel
521 : }
522 11 : uint32_t peerRank = ctx.rankIds[m];
523 11 : std::vector<HcclChannelDesc> channelDescs(channelCnt);
524 11 : HcclResult ret = HcclChannelDescInit(channelDescs.data(), channelCnt);
525 11 : CHK_PRT_RET(ret != HCCL_SUCCESS, HCCL_ERROR("[%s] HcclChannelDescInit failed, ret[%d]", __func__, ret), ret);
526 20 : for (uint32_t c = 0; c < channelCnt; c++) {
527 11 : ret = FillChannelDescForPeer(comm, team, selfRank, peerRank, channelDescs[c]);
528 11 : CHK_PRT_RET(
529 : ret != HCCL_SUCCESS,
530 : HCCL_ERROR("[%s] FillChannelDescForPeer failed, peerRank[%u] ret[%d]", __func__, peerRank, ret), ret);
531 9 : channelDescs[c].notifyNum = desc->notifyNum;
532 9 : if (!ctx.memHandles.empty()) {
533 9 : channelDescs[c].memHandles = ctx.memHandles.data();
534 9 : channelDescs[c].memHandleNum = static_cast<uint32_t>(ctx.memHandles.size());
535 : }
536 : }
537 9 : ctx.channelsByMember[m].assign(channelCnt, 0);
538 9 : ret = HcclChannelAcquire(comm, desc->engine, channelDescs.data(), channelCnt, ctx.channelsByMember[m].data());
539 9 : CHK_PRT_RET(
540 : ret != HCCL_SUCCESS,
541 : HCCL_ERROR(
542 : "[%s] HcclChannelAcquire failed, peerMember[%u] peerRank[%u] ret[%d]", __func__, m, peerRank, ret),
543 : ret);
544 11 : }
545 8 : return HCCL_SUCCESS;
546 : }
547 :
548 : /* 构造 HcommTeamBindChannelsDesc,绑定 team 与 channel。 */
549 8 : static HcclResult BindTeamChannels(
550 : HcommTeamHandle team, const HcclTeamCreateChannelsDesc* desc, const std::string& commId, ChannelsCreateCtx& ctx)
551 : {
552 8 : std::vector<uint32_t> channelNumPerMember(ctx.memberNum, 0);
553 8 : std::vector<uint64_t*> channelsByMemberIdPtrs(ctx.memberNum, nullptr);
554 : // 保持各 member 的 channel 数组生命周期覆盖 BindChannels 调用
555 24 : for (uint32_t m = 0; m < ctx.memberNum; m++) {
556 16 : channelNumPerMember[m] = static_cast<uint32_t>(ctx.channelsByMember[m].size());
557 16 : channelsByMemberIdPtrs[m] = ctx.channelsByMember[m].data();
558 : }
559 8 : HcommTeamBindChannelsDesc bindDesc = {};
560 8 : (void)HcommTeamBindChannelsDescInit(&bindDesc);
561 8 : bindDesc.memberNum = ctx.memberNum;
562 8 : bindDesc.channelNumPerMember = channelNumPerMember.data();
563 8 : bindDesc.channelsByMemberId = channelsByMemberIdPtrs.data();
564 8 : HcommResult hRet = HcommTeamBindChannels(team, &bindDesc);
565 8 : CHK_PRT_RET(
566 : hRet != 0,
567 : HCCL_ERROR(
568 : "[%s] HcommTeamBindChannels failed, comm[%s] ret[%d] memberNum[%u]", __func__, commId.c_str(), hRet,
569 : ctx.memberNum),
570 : static_cast<HcclResult>(hRet));
571 7 : return HCCL_SUCCESS;
572 8 : }
573 :
574 : /* 收集所有 peer channel 的远端内存+tag。selfMemberId 槽填本地 syncMem,peer 槽按 memberId 填远端 syncMem;
575 : * 各 window 的 localMem 远端按 (windowIndex, worldMemberId) 存 remoteMemsByWindow。 */
576 7 : static HcclResult CollectRemoteMems(HcclComm comm, HcommTeamHandle team, ChannelsCreateCtx& ctx)
577 : {
578 7 : ctx.syncMemRemoteMems.assign(ctx.memberNum, CommMem{});
579 : // selfMemberId 槽填本地 syncMem 内存
580 7 : void* localSyncMemPtr = HcclTeamMgr::GetInstance().GetSyncMemPtr(team);
581 7 : uint64_t localSyncMemSize = HcclTeamMgr::GetInstance().GetSyncMemSize(team);
582 7 : if (localSyncMemPtr != nullptr && localSyncMemSize > 0) {
583 7 : ctx.syncMemRemoteMems[ctx.selfMemberId].type = COMM_MEM_TYPE_DEVICE;
584 7 : ctx.syncMemRemoteMems[ctx.selfMemberId].addr = localSyncMemPtr;
585 7 : ctx.syncMemRemoteMems[ctx.selfMemberId].size = localSyncMemSize;
586 : }
587 : // window.mems 维度为 worldTeam memberNum,peer 槽按 worldMemberId 索引
588 14 : ctx.remoteMemsByWindow.assign(ctx.windows.size(), std::vector<CommMem>(ctx.worldMemberNum));
589 19 : for (uint32_t m = 0; m < ctx.memberNum; m++) {
590 13 : if (m == ctx.selfMemberId || ctx.channelsByMember[m].empty()) {
591 6 : continue;
592 : }
593 7 : uint32_t peerWorldMemberId = ctx.curToWorld[m];
594 13 : for (ChannelHandle channel : ctx.channelsByMember[m]) {
595 7 : if (channel == 0) {
596 0 : continue;
597 : }
598 7 : uint32_t memNum = 0;
599 7 : CommMem* remoteMems = nullptr;
600 7 : char** memTags = nullptr;
601 7 : HcclResult gRet = HcclChannelGetRemoteMems(comm, channel, &memNum, &remoteMems, &memTags);
602 7 : CHK_PRT_RET(
603 : gRet != HCCL_SUCCESS,
604 : HCCL_ERROR("[%s] HcclChannelGetRemoteMems failed, member[%u] ret[%d]", __func__, m, gRet), gRet);
605 6 : uint32_t userMemFilled = 0;
606 9 : for (uint32_t r = 0; r < memNum; r++) {
607 : std::string tag
608 9 : = (memTags != nullptr && memTags[r] != nullptr) ? std::string(memTags[r]) : std::string();
609 3 : if (!ctx.syncMemTag.empty()
610 3 : && tag.compare(0, strlen(HCCL_TEAM_SYNCMEM_TAG_PREFIX), HCCL_TEAM_SYNCMEM_TAG_PREFIX) == 0) {
611 0 : ctx.syncMemRemoteMems[m] = remoteMems[r]; // 按下标 memberId 存
612 0 : continue;
613 : }
614 3 : if (userMemFilled < ctx.windows.size()
615 3 : && tag.compare(0, strlen(HCCL_TEAM_USERMEM_TAG_PREFIX), HCCL_TEAM_USERMEM_TAG_PREFIX) == 0) {
616 1 : ctx.remoteMemsByWindow[userMemFilled][peerWorldMemberId] = remoteMems[r];
617 1 : userMemFilled++;
618 : }
619 3 : }
620 : }
621 : }
622 6 : return HCCL_SUCCESS;
623 : }
624 :
625 : /* per window 调 HcommTeamWindowBindRemoteMems(worldMemberId 维 mems);调 HcommTeamBindRemoteSyncMem 绑定 syncMem。 */
626 6 : static HcclResult BindWindowsAndSyncMem(HcommTeamHandle team, const std::string& commId, ChannelsCreateCtx& ctx)
627 : {
628 : // 对每个 window 调 HcommTeamWindowBindRemoteMems
629 11 : for (size_t w = 0; w < ctx.windows.size(); w++) {
630 6 : std::vector<CommMem> windowMems(ctx.worldMemberNum);
631 18 : for (uint32_t m = 0; m < ctx.worldMemberNum; m++) {
632 12 : if (m == ctx.worldSelfMemberId) {
633 6 : continue;
634 : }
635 6 : windowMems[m] = ctx.remoteMemsByWindow[w][m];
636 : }
637 : // self 槽填调用者在 WindowRegister 时传入的本地内存(registeredLocalMem),与 peer 槽的远端 CommMem 对称
638 6 : windowMems[ctx.worldSelfMemberId] = ctx.windows[w].registeredLocalMem;
639 6 : HcommTeamWindowDesc winDesc = {};
640 6 : (void)HcommTeamWindowDescInit(&winDesc);
641 6 : winDesc.mems = windowMems.data();
642 6 : winDesc.memberNum = ctx.worldMemberNum;
643 6 : HcommResult bindWinRet = HcommTeamWindowBindRemoteMems(team, ctx.windows[w].handle, &winDesc);
644 6 : CHK_PRT_RET(
645 : bindWinRet != 0,
646 : HCCL_ERROR(
647 : "[%s] HcommTeamWindowBindRemoteMems failed, comm[%s] team[%p] window[%p] ret[%d]", __func__,
648 : commId.c_str(), team, ctx.windows[w].handle, bindWinRet),
649 : static_cast<HcclResult>(bindWinRet));
650 6 : }
651 : // 调 HcommTeamBindRemoteSyncMem
652 5 : HcommTeamBindSyncMemDesc syncDesc = {};
653 5 : (void)HcommTeamBindSyncMemDescInit(&syncDesc);
654 5 : syncDesc.remoteMems = ctx.syncMemRemoteMems.data();
655 5 : syncDesc.remoteMemNum = static_cast<uint32_t>(ctx.syncMemRemoteMems.size());
656 5 : HcommResult bindSyncRet = HcommTeamBindRemoteSyncMem(team, &syncDesc);
657 5 : CHK_PRT_RET(
658 : bindSyncRet != 0,
659 : HCCL_ERROR(
660 : "[%s] HcommTeamBindRemoteSyncMem failed, comm[%s] team[%p] ret[%d] remoteMemNum[%zu]", __func__,
661 : commId.c_str(), team, bindSyncRet, ctx.syncMemRemoteMems.size()),
662 : static_cast<HcclResult>(bindSyncRet));
663 4 : return HCCL_SUCCESS;
664 : }
665 :
666 16 : HcclResult HcclTeamChannelsCreate(HcclComm comm, HcommTeamHandle team, const HcclTeamCreateChannelsDesc* desc)
667 : {
668 16 : CHK_PTR_NULL(comm);
669 15 : CHK_PTR_NULL(team);
670 14 : CHK_PTR_NULL(desc);
671 13 : CHK_PRT_RET(desc->channelCnt == 0, HCCL_ERROR("[%s] channelCnt is 0", __func__), HCCL_E_PARA);
672 :
673 12 : auto* hcclComm = static_cast<hccl::hcclComm*>(comm);
674 12 : CollComm* collComm = hcclComm->GetCollComm();
675 12 : CHK_PTR_NULL(collComm);
676 12 : const std::string commId = collComm->GetCommId();
677 12 : uint32_t selfRank = collComm->GetMyRankId();
678 :
679 12 : CollComm* worldCollComm = HcclTeamMgr::GetInstance().FindCollComm(team);
680 12 : CHK_PTR_NULL(worldCollComm);
681 11 : CHK_PRT_RET(
682 : worldCollComm->GetCommId() != commId,
683 : HCCL_ERROR("[%s] team[%p] is not belong to comm[%s]", __func__, team, commId.c_str()), HCCL_E_PARA);
684 :
685 11 : ChannelsCreateCtx ctx{};
686 11 : CHK_RET(GetTeamMemberInfo(team, selfRank, ctx));
687 :
688 : /* 注册 team 的 syncMem 内存(team 粒度,仅首次注册一次),供 channel 交换与 GetWorldTeamContext 取用。 */
689 11 : auto myRank = collComm->GetMyRank();
690 11 : CHK_PTR_NULL(myRank);
691 11 : CommMems* commMem = myRank->GetCommMems();
692 11 : CHK_PTR_NULL(commMem);
693 11 : HcclMemHandle syncMemHandle = nullptr;
694 11 : CHK_RET(RegisterTeamSyncMem(team, commId, commMem, syncMemHandle));
695 :
696 11 : CHK_RET(GetWorldTeamContext(team, selfRank, ctx));
697 11 : CHK_RET(AcquireChannels(comm, team, desc, selfRank, ctx));
698 8 : CHK_RET(BindTeamChannels(team, desc, commId, ctx));
699 7 : CHK_RET(CollectRemoteMems(comm, team, ctx));
700 6 : CHK_RET(BindWindowsAndSyncMem(team, commId, ctx));
701 :
702 4 : HCCL_INFO(
703 : "[%s] success, comm[%s] team[%p] memberNum[%u] channelCnt[%u] windowNum[%zu] syncMemRemoteMemNum[%zu]",
704 : __func__, commId.c_str(), team, ctx.memberNum, desc->channelCnt, ctx.windows.size(),
705 : ctx.syncMemRemoteMems.size());
706 4 : return HCCL_SUCCESS;
707 12 : }
|