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