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 <cmath>
12 : #include <algorithm>
13 : #include "device_capacity.h"
14 : #include "coll_executor_base.h"
15 : #include "coll_alg_exec_registry.h"
16 : #include "coll_alg_operator.h"
17 :
18 : namespace hccl {
19 : using namespace std;
20 : constexpr float GB2B = 1024 * 1024 * 1024;
21 : constexpr float SECOND2MICROSECOND = 1000000;
22 : constexpr float RHD_FACTOR_TWO = 2.0;
23 : constexpr float RHD_FACTOR_ONE = 1.0;
24 : constexpr float DOUBLE_SUB_HCCLCMD = 2.0; // The hcclCMD can be considered as combination of two hcclCMDs.
25 : constexpr float COPY_TIME_IN_RHD = 1.0;
26 : constexpr double NHR_FACTOR_TWO = 2.0;
27 : constexpr double NHR_FACTOR_THREE = 3.0;
28 : constexpr double NHR_FACTOR_FOUR = 4.0;
29 : constexpr double NHR_SUB_TWO = 2.0;
30 : constexpr float LATENCY = 60; // 静态时延 60 us;
31 : constexpr u64 PIPELINE_MIN_SIZE = 32 * 1024; // 当数据量大于等于32KB时,reduce_scatter和all_gather使能pipeline模式
32 : constexpr u64 PIPELINE_ALLREDUCE_MIN_SIZE = 1024 * 1024; // 当数据量大于等于1MB时,allreduce使能pipeline模式
33 : constexpr u64 PIPELINE_MIN_SIZE_NO_LITE = 2 * 1024 * 1024; // 如不支持RDMALite,当数据量大于等于2MB时,使能pipeline模式
34 : constexpr u64 HCCL_FFTS_CAPACITY = 65535; // FFTS+子图最大容量
35 : constexpr u32 AHC_MIN_SUBGROUP_SPLIT_DIVISOR = 2;
36 : constexpr u32 AHC_LEVEL0_GROUP_SIZE_THRESHOLD = 3;
37 : constexpr u32 SERVER_COUNT_THRESHOLD_FOR_MULTI_DETER_PIPELINE = 2;
38 : constexpr u32 MIN_STRICT_RANK_NUM = 3;
39 :
40 87 : CollAlgOperator::CollAlgOperator(
41 : AlgConfigurator* algConfigurator, CCLBufferManager& cclBufferManager, HcclDispatcher dispatcher,
42 87 : std::unique_ptr<TopoMatcher>& topoMatcher, HcclCMDType opType)
43 84 : : algConfigurator_(algConfigurator),
44 84 : cclBufferManager_(cclBufferManager),
45 80 : dispatcher_(dispatcher),
46 80 : topoMatcher_(topoMatcher),
47 559 : workflowMode_(GetWorkflowMode())
48 : {
49 75 : SetTopoAttr(algConfigurator_);
50 78 : SetAlgoAttr(algConfigurator_);
51 94 : algConfigurator->GetAlgTypeDirect(algType_, opType);
52 74 : algConfigurator->GetAlgoLevel1DefaultSwitch(isAlgoLevel1Default_, opType);
53 73 : algConfigurator->GetTopoType(topoType_);
54 73 : }
55 :
56 : HcclResult
57 0 : CollAlgOperator::SelectAlg(const std::string& tag, const OpParam& param, std::string& algName, std::string& newTag)
58 : {
59 0 : return HCCL_SUCCESS;
60 : }
61 :
62 30 : HcclResult CollAlgOperator::SelectAlg(
63 : const std::string& tag, const OpParam& param, std::string& algName, std::string& newTag, const ResourceLimit& limit)
64 : {
65 30 : return SelectAlg(tag, param, algName, newTag);
66 : }
67 :
68 1 : HcclResult CollAlgOperator::GetAivExecParam(
69 : std::string& algName, const OpParam& param, AlgResourceResponse& algRes, AivSuperKernelArgs& args)
70 : {
71 1 : if (executor_.get() == nullptr) {
72 0 : executor_ = CollAlgExecRegistry::Instance().GetAlgExec(algName, dispatcher_, topoMatcher_);
73 0 : CHK_PRT_RET(
74 : executor_.get() == nullptr,
75 : HCCL_ERROR("[CollAlgOperator][GetAivExecParam]Fail to find executor for algName[%s]", algName.c_str()),
76 : HCCL_E_PARA);
77 : }
78 1 : return executor_->GetAivExecParam(param, algRes, args);
79 : }
80 :
81 : HcclResult
82 1 : CollAlgOperator::CalNumBlocks(std::string& algName, const OpParam& param, u32& numBlocks, int32_t aivCoreLimit)
83 : {
84 1 : if (executor_.get() == nullptr) {
85 0 : executor_ = CollAlgExecRegistry::Instance().GetAlgExec(algName, dispatcher_, topoMatcher_);
86 0 : CHK_PRT_RET(
87 : executor_.get() == nullptr,
88 : HCCL_ERROR("[CollAlgOperator][CalNumBlocks]Fail to find executor for algName[%s]", algName.c_str()),
89 : HCCL_E_PARA);
90 0 : CHK_RET(SetExecutorAttr(param));
91 : }
92 :
93 1 : if (aivCoreLimit != 0) {
94 1 : CHK_RET(executor_->SetNumBlocks(aivCoreLimit));
95 : }
96 :
97 1 : if (param.opType == HcclCMDType::HCCL_CMD_ALLTOALL) {
98 0 : return executor_->CalNumBlocks(
99 0 : numBlocks, userRankSize_, param.All2AllDataDes.sendCount * SIZE_TABLE[param.All2AllDataDes.sendType],
100 0 : param.opType);
101 1 : } else if (
102 1 : param.opType == HcclCMDType::HCCL_CMD_ALLREDUCE || param.opType == HcclCMDType::HCCL_CMD_REDUCE_SCATTER
103 0 : || param.opType == HcclCMDType::HCCL_CMD_ALLGATHER || param.opType == HcclCMDType::HCCL_CMD_BROADCAST) {
104 1 : return executor_->CalNumBlocks(
105 1 : numBlocks, userRankSize_, param.DataDes.count * SIZE_TABLE[param.DataDes.dataType], param.opType);
106 : } else {
107 0 : return executor_->CalNumBlocks(numBlocks, userRankSize_);
108 : }
109 : return HCCL_SUCCESS;
110 : }
111 :
112 0 : HcclResult CollAlgOperator::GetOpExpansionStr(const OpParam& param, const AlgDesc& algDesc, std::string& opExpansionStr)
113 : {
114 0 : if (algDesc.isAivMode) {
115 0 : opExpansionStr = "AIV";
116 0 : } else if (param.aicpuUnfoldMode) {
117 0 : opExpansionStr = "AI_CPU";
118 0 : } else if (static_cast<bool>(topoMatcher_->GetExternalInputHcclEnableFfts())) {
119 0 : opExpansionStr = "HOST";
120 : } else {
121 0 : opExpansionStr = "HOST_TS";
122 : }
123 0 : return HCCL_SUCCESS;
124 : }
125 :
126 60 : HcclResult CollAlgOperator::SelectAlg(
127 : const std::string& tag, const OpParam& param, const ResourceLimit& limit, std::string& algName, AlgDesc& algDesc,
128 : std::string& newTag)
129 : {
130 60 : bool isOnlyAiv = topoMatcher_->GetIsOnlyAivConfig();
131 62 : bool supportOnlyAiv
132 54 : = (param.opType == HcclCMDType::HCCL_CMD_ALLGATHER || param.opType == HcclCMDType::HCCL_CMD_REDUCE_SCATTER
133 28 : || param.opType == HcclCMDType::HCCL_CMD_ALLTOALLV || param.opType == HcclCMDType::HCCL_CMD_ALLTOALLVC
134 116 : || param.opType == HcclCMDType::HCCL_CMD_ALLTOALL || param.opType == HcclCMDType::HCCL_CMD_ALLREDUCE);
135 62 : CHK_PRT_RET(
136 : isOnlyAiv && !supportOnlyAiv,
137 : HCCL_ERROR(
138 : "[CollAlgOperator][SelectAlg] opType[%s] currently do not support aivonly",
139 : GetCMDTypeEnumStr(param.opType).c_str()),
140 : HCCL_E_NOT_SUPPORT);
141 62 : CHK_PRT_RET(
142 : isOnlyAiv && userRankSize_ == 1 && supportOnlyAiv,
143 : HCCL_ERROR("[CollAlgOperator][SelectAlg] aivonly not support, please ensure rankNum is greater than one"),
144 : HCCL_E_NOT_SUPPORT);
145 :
146 : // 兼容老接口
147 62 : if (limit.ifLimit) {
148 62 : CHK_RET(SelectAlg(tag, param, algName, newTag, limit));
149 : } else {
150 0 : CHK_RET(SelectAlg(tag, param, algName, newTag));
151 : }
152 :
153 : // 非AIV算法提前返回, 采用兜底Executor
154 76 : if (algName.empty()) {
155 0 : executor_ = CollAlgExecRegistry::Instance().GetAlgExec("SendExecutor", dispatcher_, topoMatcher_);
156 0 : CHK_PRT_RET(
157 : executor_.get() == nullptr,
158 : HCCL_ERROR("[CollAlgOperator][SelectAlg]Fail to find executor for algName[DefaultExecutor]"), HCCL_E_PARA);
159 : } else {
160 : // 校验控核
161 75 : if (limit.ifLimit && deviceType_ == DevType::DEV_TYPE_910_93 && topoMatcher_->GetAivModeConfig()) {
162 0 : CHK_RET(SelectAlgFor91093WithCoreLimit(param, limit, algName));
163 : }
164 :
165 : // 从对应executor获取算法描述
166 74 : if (executor_.get() == nullptr) {
167 72 : executor_ = CollAlgExecRegistry::Instance().GetAlgExec(algName, dispatcher_, topoMatcher_);
168 73 : CHK_PRT_RET(
169 : executor_.get() == nullptr,
170 : HCCL_ERROR("[CollAlgOperator][SelectAlg]Fail to find executor for algName[%s]", algName.c_str()),
171 : HCCL_E_PARA);
172 71 : CHK_RET(SetExecutorAttr(param));
173 : }
174 : }
175 :
176 72 : bool isLastSelect = algDesc.isLastSelect;
177 72 : algDesc = executor_->GetAlgDesc();
178 : // 打印维测日志
179 69 : if (UNLIKELY(GetDebugConfig() & HCCL_ALG) && isLastSelect) {
180 : // 获取展开模式,转换成字符串
181 0 : std::string opExpansionStr;
182 0 : CHK_RET(GetOpExpansionStr(param, algDesc, opExpansionStr));
183 : // 尝试获取确定性属性(如果Executor有声明自己是否为确定性)
184 0 : std::string appendStr = "";
185 0 : if (algDesc.deterministic >= 0) {
186 0 : appendStr += "deterministic[" + std::to_string(algDesc.deterministic) + "]";
187 : }
188 : // 打印关键维测内容
189 0 : bool isOpBase = GetWorkflowMode() == HcclWorkflowMode::HCCL_WORKFLOW_MODE_OP_BASE;
190 0 : HCCL_CONFIG_INFO(
191 : HCCL_ALG,
192 : "[%s] newTag[%s] algName[%s] userRank[%u] topoType[%d] algType[%s] "
193 : "userRankSize[%u] level0Size[%u] moduleNum_[%u] level2Size[%u] ",
194 : __func__, newTag.c_str(), algName.c_str(), userRank_, topoType_, AlgTypeToStr(algDesc.algType).c_str(),
195 : userRankSize_, deviceNumPerAggregation_, moduleNum_, superPodNum_);
196 0 : HCCL_CONFIG_INFO(
197 : HCCL_ALG,
198 : "[%s] newTag[%s] "
199 : "opExpansionMode[%s] isZeroCopy[%u] retryEnable[%u] isOpBase[%u] isCapture[%u] aivCoreLimit[%u] %s.",
200 : __func__, newTag.c_str(), opExpansionStr.c_str(), algDesc.isZeroCopy, retryEnable_, isOpBase,
201 : param.isCapture, limit.aivCoreLimit, appendStr.c_str());
202 0 : }
203 72 : return HCCL_SUCCESS;
204 : }
205 :
206 : HcclResult
207 0 : CollAlgOperator::SelectAlgFor91093WithCoreLimit(const OpParam& param, const ResourceLimit& limit, std::string& algName)
208 : {
209 0 : if (executor_.get() == nullptr) {
210 0 : executor_ = CollAlgExecRegistry::Instance().GetAlgExec(algName, dispatcher_, topoMatcher_);
211 0 : CHK_PRT_RET(
212 : executor_.get() == nullptr,
213 : HCCL_ERROR(
214 : "[CollAlgOperator][SelectAlgFor91093WithCoreLimit]Fail to find executor for algName[%s]",
215 : algName.c_str()),
216 : HCCL_E_PARA);
217 0 : CHK_RET(SetExecutorAttr(param));
218 : }
219 :
220 0 : CHK_RET(SetNumBlocks(limit.aivCoreLimit));
221 :
222 0 : std::string reSelName;
223 0 : switch (param.opType) {
224 0 : case HcclCMDType::HCCL_CMD_ALLREDUCE:
225 0 : reSelName = "AllReduceMeshAivFor91093Executor";
226 0 : break;
227 0 : case HcclCMDType::HCCL_CMD_ALLGATHER:
228 0 : reSelName = "AllGatherMeshAivFor91093Executor";
229 0 : break;
230 0 : case HcclCMDType::HCCL_CMD_REDUCE_SCATTER:
231 0 : reSelName = "ReduceScatterMeshAivFor91093Executor";
232 0 : break;
233 0 : case HcclCMDType::HCCL_CMD_ALLTOALLV:
234 : case HcclCMDType::HCCL_CMD_ALLTOALL:
235 : case HcclCMDType::HCCL_CMD_ALLTOALLVC:
236 0 : reSelName = "AlltoAllMeshAivFor91093Executor";
237 0 : break;
238 0 : default:
239 0 : break;
240 : }
241 :
242 : u32 numBlocks;
243 0 : HcclResult ret = CalNumBlocks(algName, param, numBlocks);
244 0 : if (ret != HCCL_SUCCESS) {
245 0 : CHK_PRT_RET(
246 : reSelName.empty() || reSelName == algName,
247 : HCCL_ERROR(
248 : "[CollAlgOperator][SelectAlgFor91093WithCoreLimit]Fail to check CalNumBlocks for algName[%s]",
249 : algName.c_str()),
250 : HCCL_E_PARA);
251 :
252 0 : algName = reSelName;
253 0 : executor_ = nullptr;
254 0 : HCCL_INFO("[CollAlgOperator][SelectAlgFor91093WithCoreLimit]Re select to algName[%s]", reSelName.c_str());
255 : }
256 :
257 0 : return HCCL_SUCCESS;
258 0 : }
259 :
260 : HcclResult
261 70 : CollAlgOperator::CalcResRequest(const std::string& algName, const OpParam& param, AlgResourceRequest& resourceRequest)
262 : {
263 70 : if (executor_.get() == nullptr) {
264 0 : executor_ = CollAlgExecRegistry::Instance().GetAlgExec(algName, dispatcher_, topoMatcher_);
265 0 : CHK_PRT_RET(
266 : executor_.get() == nullptr,
267 : HCCL_ERROR("[CollAlgOperator][CalcResRequest]Fail to find executor for algName[%s]", algName.c_str()),
268 : HCCL_E_PARA);
269 0 : CHK_RET(SetExecutorAttr(param));
270 : }
271 69 : return executor_->CalcResRequest(param, resourceRequest);
272 : }
273 :
274 24 : HcclResult CollAlgOperator::Orchestrate(const std::string& algName, OpParam& param, AlgResourceResponse& algResource)
275 : {
276 24 : HCCL_INFO("[CollAlgOperator][Orchestrate]algName[%s]", algName.c_str());
277 24 : if (executor_.get() == nullptr) {
278 0 : executor_ = CollAlgExecRegistry::Instance().GetAlgExec(algName, dispatcher_, topoMatcher_);
279 0 : CHK_PRT_RET(
280 : executor_.get() == nullptr,
281 : HCCL_ERROR("[CollAlgOperator][Orchestrate]Fail to find executor for algName[%s]", algName.c_str()),
282 : HCCL_E_PARA);
283 0 : CHK_RET(SetExecutorAttr(param));
284 : }
285 24 : executor_->SetAivClearEnable(aivClearEnable_);
286 24 : executor_->SetAlgOpContext(algOpContext_);
287 24 : executor_->SetOpCounter(opCounter_);
288 24 : return executor_->Orchestrate(param, algResource);
289 : }
290 :
291 0 : HcclResult CollAlgOperator::GetAdjInfo(
292 : const std::string& algName, OpParam& param, AlgResourceResponse& algResource, AdjInfo& nslbAdjInfo)
293 : {
294 0 : if (executor_.get() == nullptr) {
295 0 : executor_ = CollAlgExecRegistry::Instance().GetAlgExec(algName, dispatcher_, topoMatcher_);
296 0 : CHK_PRT_RET(
297 : executor_.get() == nullptr,
298 : HCCL_ERROR("[CollAlgOperator][Orchestrate]Fail to find executor for algName[%s]", algName.c_str()),
299 : HCCL_E_PARA);
300 0 : CHK_RET(SetExecutorAttr(param));
301 : }
302 :
303 0 : return executor_->GetAdjInfo(algResource, nslbAdjInfo);
304 : }
305 :
306 25 : HcclResult CollAlgOperator::PrepareCommInfoToDevice(const std::string& algName, AlgResourceResponse& algResource)
307 : {
308 25 : if (executor_.get() == nullptr) {
309 0 : executor_ = CollAlgExecRegistry::Instance().GetAlgExec(algName, dispatcher_, topoMatcher_);
310 0 : CHK_PRT_RET(
311 : executor_.get() == nullptr,
312 : HCCL_ERROR(
313 : "[CollAlgOperator][PrepareCommInfoToDevice]Fail to find executor for algName[%s]", algName.c_str()),
314 : HCCL_E_PARA);
315 : }
316 25 : return executor_->PrepareCommInfoToDevice(algResource);
317 : }
318 :
319 0 : HcclResult CollAlgOperator::CalcIncreLinkRequest(
320 : const std::string& algName, const OpParam& param, std::set<u32>& ranksHasLinked,
321 : AlgResourceRequest& resourceRequest, bool& needIncreLink)
322 : {
323 0 : if (executor_.get() == nullptr) {
324 0 : executor_ = CollAlgExecRegistry::Instance().GetAlgExec(algName, dispatcher_, topoMatcher_);
325 0 : CHK_PRT_RET(
326 : executor_.get() == nullptr,
327 : HCCL_ERROR("[CollAlgOperator][CalcIncreLinkRequest]Fail to find executor for algName[%s]", algName.c_str()),
328 : HCCL_E_PARA);
329 : }
330 0 : return executor_->CalcIncreLinkRequest(param, ranksHasLinked, resourceRequest, needIncreLink);
331 : }
332 :
333 77 : void CollAlgOperator::SetTopoAttr(AlgConfigurator* algConfigurator)
334 : {
335 77 : const HcclTopoAttr& topoAttr = algConfigurator->GetTopoAttr();
336 78 : serverNum_ = topoAttr.serverNum;
337 78 : moduleNum_ = topoAttr.moduleNum;
338 78 : superPodNum_ = topoAttr.superPodNum;
339 78 : deviceNumPerServer_ = topoAttr.deviceNumPerServer;
340 78 : deviceNumPerAggregation_ = topoAttr.deviceNumPerAggregation;
341 78 : multiModuleDiffDeviceNumMode_ = topoAttr.multiModuleDiffDeviceNumMode;
342 78 : multiSuperPodDiffServerNumMode_ = topoAttr.multiSuperPodDiffServerNumMode;
343 78 : multiSuperPodDiffDeviceNumMode_ = topoAttr.multiSuperPodDiffDeviceNumMode;
344 78 : isDiffDeviceType_ = topoAttr.isDiffDeviceType;
345 78 : gcdDeviceNumPerAggregation_ = topoAttr.gcdDeviceNumPerAggregation;
346 :
347 78 : meshAggregationRankSize_ = topoAttr.meshAggregationRankSize;
348 78 : isDiffDeviceModule_ = topoAttr.isDiffDeviceModule;
349 78 : isSingleMeshAggregation_ = topoAttr.isSingleMeshAggregation;
350 78 : isAllRankSamePlane_ = topoAttr.isAllRankSamePlane;
351 78 : is310PDuoCard_ = topoAttr.is310PDuoCard;
352 78 : isCommon310P3DUO_ = topoAttr.isCommon310P3DUO;
353 78 : hccsPortNum_ = topoAttr.hccsPortNum;
354 :
355 78 : userRank_ = topoAttr.userRank;
356 78 : realUserRank_ = topoAttr.realUserRank;
357 78 : userRankSize_ = topoAttr.userRankSize;
358 :
359 78 : devicePhyId_ = topoAttr.devicePhyId;
360 78 : deviceLogicId_ = topoAttr.deviceLogicId;
361 78 : deviceType_ = topoAttr.deviceType;
362 :
363 78 : nicList_ = topoAttr.nicList;
364 78 : pairLinkCounter_ = topoAttr.pairLinkCounter;
365 78 : isSupportRdmaLite_ = topoAttr.isSupportRdmaLite;
366 78 : isSupportHccsAndSio_ = topoAttr.isSupportHccsAndSio;
367 78 : useSuperPodMode_ = topoAttr.useSuperPodMode;
368 78 : isARSDoubleRing_ = topoAttr.isARSDoubleRing;
369 78 : return;
370 : }
371 :
372 80 : void CollAlgOperator::SetAlgoAttr(AlgConfigurator* algConfigurator)
373 : {
374 80 : const HcclAlgoAttr& algoAttr = algConfigurator->GetAlgoAttr();
375 80 : isHaveCpuRank_ = algoAttr.isHaveCpuRank;
376 80 : inlineReduceSwitchOn_ = algoAttr.inlineReduceSwitchOn;
377 80 : identifier_ = algoAttr.identifier;
378 93 : return;
379 : }
380 :
381 66 : HcclResult CollAlgOperator::SetExecutorAttr(const OpParam& param)
382 : {
383 66 : CHK_RET(executor_->SetAlgType(algType_));
384 64 : CHK_RET(executor_->SetCCLInBuffer(cclBufferManager_.GetInCCLbufferSize()));
385 :
386 65 : if (param.opType == HcclCMDType::HCCL_CMD_REDUCE_SCATTER) {
387 31 : bool isSupportSDMAReduce = false;
388 31 : if (GetWorkflowMode() != HcclWorkflowMode::HCCL_WORKFLOW_MODE_OP_BASE) {
389 : isSupportSDMAReduce
390 13 : = IsSupportSDMAReduce(param.inputPtr, param.outputPtr, param.DataDes.dataType, param.reduceType);
391 : } else {
392 17 : isSupportSDMAReduce = IsSupportSDMAReduce(
393 17 : cclBufferManager_.GetInCCLbuffer().ptr(), cclBufferManager_.GetOutCCLbuffer().ptr(),
394 17 : param.DataDes.dataType, param.reduceType);
395 : }
396 30 : CHK_RET(executor_->SetIsSupportSDMAReduce(isSupportSDMAReduce));
397 : }
398 66 : return HCCL_SUCCESS;
399 : }
400 :
401 0 : std::string CollAlgOperator::GenerateNewTagByAlgTypeLevel1(std::string tag, std::string algTypeLevel1Tag) const
402 : {
403 0 : if (algTypeLevel1Tag == "") {
404 0 : return tag;
405 : } else {
406 0 : return tag + "_" + algTypeLevel1Tag;
407 : }
408 : }
409 :
410 0 : HcclResult CollAlgOperator::AppendTag(const AlgTypeLevel1& algTypeLevel1, std::string& tag)
411 : {
412 0 : switch (algTypeLevel1) {
413 0 : case AlgTypeLevel1::ALG_LEVEL1_RING:
414 0 : tag = "ALG_LEVEL1_RING";
415 0 : break;
416 0 : case AlgTypeLevel1::ALG_LEVEL1_HD:
417 0 : tag = "ALG_LEVEL1_HD";
418 0 : break;
419 0 : case AlgTypeLevel1::ALG_LEVEL1_NHR:
420 0 : tag = "ALG_LEVEL1_NHR";
421 0 : break;
422 0 : case AlgTypeLevel1::ALG_LEVEL1_PIPELINE:
423 0 : tag = "ALG_LEVEL1_PIPELINE";
424 0 : break;
425 0 : default:
426 0 : HCCL_WARNING("[CollAlgOperator][AppendTag] The algTypeLevel1 %d is not supported.", algTypeLevel1);
427 0 : break;
428 : }
429 0 : return HCCL_SUCCESS;
430 : }
431 :
432 14 : HcclResult CollAlgOperator::AutoSelectAlgTypeLevel1(
433 : HcclCMDType hcclCMDType, u64 countSize, u64 cclBufferSize, std::string& algTypeLevel1Tag, bool isInlineReduce,
434 : bool isRdmaReduce, bool isAivMode)
435 : {
436 14 : if (isSingleMeshAggregation_) {
437 14 : HCCL_INFO(
438 : "[AutoSelectAlgTypeLevel1] there are %u server(%u module) in level1, no need to choose algo.", serverNum_,
439 : moduleNum_);
440 18 : return HCCL_SUCCESS;
441 : }
442 :
443 : // auto algo selection process
444 0 : if (isAlgoLevel1Default_) {
445 : // parse algType_ and get algTypeLevel1 and algTypeLevel0
446 0 : auto originalAlgTypeLevel0 = algType_.algoLevel0;
447 : // set algTypeLevel1
448 : AlgTypeLevel1 algTypeLevel1;
449 0 : CHK_RET(GetDefaultAlgoLevel1V2(
450 : hcclCMDType, countSize, cclBufferSize, algTypeLevel1, isInlineReduce, isRdmaReduce, isAivMode));
451 0 : auto iter = HCCL_ALGO_LEVEL1_NAME_MAP.find(algTypeLevel1);
452 0 : CHK_PRT_RET(
453 : iter == HCCL_ALGO_LEVEL1_NAME_MAP.end(),
454 : HCCL_ERROR("[AutoSelectAlgTypeLevel1] level1: algType[%u] is invalid.", algTypeLevel1), HCCL_E_INTERNAL);
455 0 : HCCL_INFO(
456 : "[AutoSelectAlgTypeLevel1] there are %u server(%u module) in level1, using %s algo", serverNum_, moduleNum_,
457 : iter->second.c_str());
458 0 : algType_.algoLevel0 = originalAlgTypeLevel0;
459 0 : algType_.algoLevel1 = algTypeLevel1;
460 : // tag 增加所选的算法
461 0 : AppendTag(algTypeLevel1, algTypeLevel1Tag);
462 : }
463 0 : return HCCL_SUCCESS;
464 : }
465 :
466 0 : HcclResult CollAlgOperator::SelectAlgoForComm(
467 : HcclCMDType hcclCMDType, float delay, u64 curSize, float bandWidth, AlgTypeLevel1& algType)
468 : {
469 : // 从map中查找对应的计算函数
470 0 : auto it = selectFuncMap_.find(hcclCMDType);
471 0 : if (it == selectFuncMap_.end()) {
472 0 : HCCL_ERROR("[Get][AlgTypeLevel1] The hcclCMDType %d is not supported.", hcclCMDType);
473 0 : return HCCL_E_NOT_SUPPORT;
474 : }
475 0 : return (it->second)(delay, curSize, bandWidth, algType);
476 : }
477 :
478 : // 保守估计Pipeline算法所需context数量
479 0 : u32 CollAlgOperator::CalcContextNumForPipeline(HcclCMDType hcclCMDType)
480 : {
481 : bool isDeterPipeline
482 0 : = topoMatcher_->GetDeterministicConfig() == DETERMINISTIC_ENABLE
483 0 : && (hcclCMDType == HcclCMDType::HCCL_CMD_ALLREDUCE || hcclCMDType == HcclCMDType::HCCL_CMD_REDUCE_SCATTER);
484 0 : const u32 stepNum = moduleNum_; // 通信步数
485 0 : const u32 hccsContextNumPerStep = 5 * (deviceNumPerAggregation_ - 1); // SDMA跨片每步所需context数
486 0 : const u32 roceContextNumPerStep = 7; // RDMA每步所需context数
487 0 : const u32 copyContextNumPerStep = 1; // SDMA片内每步所需context数
488 0 : const u32 localReduceNumPerStep = isDeterPipeline ? (deviceNumPerAggregation_ - 1) : 0;
489 0 : const u32 contextNumPerStep
490 0 : = hccsContextNumPerStep + roceContextNumPerStep + copyContextNumPerStep + localReduceNumPerStep; // 小计
491 0 : const u32 barrierContextNum = 4; // 通信结束时barrier操作所需context数
492 :
493 0 : switch (hcclCMDType) {
494 0 : case HcclCMDType::HCCL_CMD_ALLREDUCE: // fall-through
495 : case HcclCMDType::HCCL_CMD_REDUCE_SCATTER: // fall-through
496 : case HcclCMDType::HCCL_CMD_ALLGATHER:
497 : case HcclCMDType::HCCL_CMD_ALLGATHER_V: {
498 0 : const u32 copyContextNum = 1; // 通信首尾所需context数量
499 0 : u32 contextNum = stepNum * contextNumPerStep + barrierContextNum + copyContextNum;
500 0 : if (hcclCMDType == HcclCMDType::HCCL_CMD_ALLREDUCE) {
501 0 : contextNum += contextNum;
502 : }
503 0 : if (isDeterPipeline) {
504 0 : contextNum += stepNum - 1; // 最后的local reduce
505 : }
506 0 : return contextNum;
507 : }
508 0 : case HcclCMDType::HCCL_CMD_ALLTOALLV: // fall-through
509 : case HcclCMDType::HCCL_CMD_ALLTOALLVC: // fall-through
510 : case HcclCMDType::HCCL_CMD_ALLTOALL: {
511 0 : const u32 copyContextNum = 1 + moduleNum_; // 通信首尾所需context数量
512 0 : return stepNum * contextNumPerStep + barrierContextNum + copyContextNum;
513 : }
514 0 : default:
515 0 : return 0;
516 : }
517 : }
518 :
519 0 : HcclResult CollAlgOperator::GetDefaultAlgoLevel1V2(
520 : HcclCMDType hcclCMDType, u64 curSize, u64 cclBufferSize, AlgTypeLevel1& algType, bool isInlineReduce,
521 : bool isRdmaReduce, bool isAivMode)
522 : {
523 : // pipeline mode is deployed,where there is multi-sever multi-device(insever) now,
524 : // since RDMA is not reduced by normal serial orchestration of tasks.
525 : // So pipeline mode is more dominant than normal serial orchestration now.
526 0 : auto originalAlgTypeLevel0 = algType_.algoLevel0;
527 : bool disdeterniminsticWithInlineReduce
528 0 : = isInlineReduce && isRdmaReduce && topoMatcher_->GetDeterministicConfig() == DETERMINISTIC_DISABLE;
529 : bool deterniminsticWithInlineReduce
530 0 : = isInlineReduce && isRdmaReduce && topoMatcher_->GetDeterministicConfig() == DETERMINISTIC_ENABLE;
531 :
532 : // 对于不支持Rdma Lite的场景,下发性能较差,RS和AG需要一个很大的数据量(AR的一半)才能掩盖下发时间
533 0 : u64 pipelineMinSize = (isSupportRdmaLite_) ? (PIPELINE_MIN_SIZE) : (PIPELINE_MIN_SIZE_NO_LITE);
534 0 : if (((hcclCMDType == HcclCMDType::HCCL_CMD_REDUCE_SCATTER && disdeterniminsticWithInlineReduce)
535 0 : || hcclCMDType == HcclCMDType::HCCL_CMD_ALLGATHER || hcclCMDType == HcclCMDType::HCCL_CMD_ALLGATHER_V)
536 0 : && deviceNumPerAggregation_ != 1 && curSize >= pipelineMinSize && IsAlgTypeLevel0Mesh(originalAlgTypeLevel0)
537 0 : && CalcContextNumForPipeline(hcclCMDType) <= HCCL_FFTS_CAPACITY) {
538 0 : algType = AlgTypeLevel1::ALG_LEVEL1_PIPELINE;
539 0 : return HCCL_SUCCESS;
540 : }
541 0 : if (hcclCMDType == HcclCMDType::HCCL_CMD_REDUCE_SCATTER && deterniminsticWithInlineReduce
542 0 : && deviceNumPerAggregation_ > 1 && curSize >= pipelineMinSize && IsAlgTypeLevel0Mesh(originalAlgTypeLevel0)
543 0 : && CalcContextNumForPipeline(hcclCMDType) <= HCCL_FFTS_CAPACITY && moduleNum_ > 1
544 0 : && curSize >= HCCL_SMALL_COUNT_256_KB) {
545 0 : algType = AlgTypeLevel1::ALG_LEVEL1_PIPELINE;
546 0 : return HCCL_SUCCESS;
547 : }
548 :
549 : // 对于不支持Rdma Lite的场景,下发性能较差,AllReduce需要一个较大的数据量才能掩盖下发时间
550 0 : pipelineMinSize = (isSupportRdmaLite_) ? (PIPELINE_ALLREDUCE_MIN_SIZE) : (PIPELINE_MIN_SIZE_NO_LITE);
551 0 : if (hcclCMDType == HcclCMDType::HCCL_CMD_ALLREDUCE) {
552 : // 计算每个slice的大小
553 0 : u64 allreduceCurSize = 0;
554 0 : allreduceCurSize = curSize / (moduleNum_ * deviceNumPerAggregation_);
555 0 : if (disdeterniminsticWithInlineReduce && deviceNumPerAggregation_ != 1 && allreduceCurSize >= pipelineMinSize
556 0 : && !isAivMode && IsAlgTypeLevel0Mesh(originalAlgTypeLevel0)
557 0 : && CalcContextNumForPipeline(hcclCMDType) <= HCCL_FFTS_CAPACITY) {
558 0 : algType = AlgTypeLevel1::ALG_LEVEL1_PIPELINE;
559 0 : return HCCL_SUCCESS;
560 : }
561 0 : if (deterniminsticWithInlineReduce && deviceNumPerAggregation_ > 1 && allreduceCurSize >= HCCL_SMALL_COUNT_1_MB
562 0 : && !isAivMode && IsAlgTypeLevel0Mesh(originalAlgTypeLevel0)
563 0 : && CalcContextNumForPipeline(hcclCMDType) <= HCCL_FFTS_CAPACITY) {
564 0 : algType = AlgTypeLevel1::ALG_LEVEL1_PIPELINE;
565 0 : return HCCL_SUCCESS;
566 : }
567 : }
568 0 : u64 dataSizePerLoop = curSize > cclBufferSize ? cclBufferSize : curSize;
569 0 : float delay = LATENCY; // 静态时延 60 us;
570 : float bandWidth;
571 0 : CHK_RET(GetBandWidthPerNPU(1, userRankSize_, deviceNumPerAggregation_, bandWidth)); // 单位:GB/s
572 0 : bandWidth = bandWidth * GB2B; // 单位:B/s
573 0 : CHK_RET(SelectAlgoForComm(hcclCMDType, delay, dataSizePerLoop, bandWidth, algType));
574 0 : return HCCL_SUCCESS;
575 : }
576 :
577 : HcclResult
578 0 : CollAlgOperator::SelectAlgoTypeForReduceScatter(float delay, u64 recvCurSize, float bandWidth, AlgTypeLevel1& algType)
579 : {
580 0 : auto steps = moduleNum_ - 1;
581 : // theoretical time cost of Ring
582 0 : double ringCost
583 0 : = static_cast<double>(steps) * delay
584 0 : + static_cast<double>(steps) / moduleNum_ * recvCurSize * userRankSize_ / bandWidth * SECOND2MICROSECOND;
585 :
586 : // theoretical time cost of NHR
587 0 : double nhrCost = ceil(log2(moduleNum_)) * delay
588 0 : + static_cast<double>(moduleNum_ - 1) / moduleNum_ * recvCurSize * userRankSize_ / bandWidth
589 0 : * SECOND2MICROSECOND;
590 :
591 : // compare costs between NHR and Ring, if same cost, Ring > NHR > HD
592 0 : algType = (nhrCost < ringCost) ? AlgTypeLevel1::ALG_LEVEL1_NHR : AlgTypeLevel1::ALG_LEVEL1_RING;
593 0 : double interMinCost = min(nhrCost, ringCost);
594 :
595 : // theoretical time cost of HD/RHD
596 0 : double hdCost = 0.0;
597 0 : if ((moduleNum_ & (moduleNum_ - 1)) == 0) {
598 : // theoretical time cost of HD
599 : hdCost
600 0 : = log2(moduleNum_) * delay
601 0 : + static_cast<double>(steps) / moduleNum_ * recvCurSize * userRankSize_ / bandWidth * SECOND2MICROSECOND;
602 : } else {
603 : // theoretical time cost of Recursive HD,
604 : // the (RHD_FACTOR_TWO * moduleNum_ - RHD_FACTOR_ONE) / moduleNum_ is equal to 1 + (moduleNum_ -1) / moduleNum_
605 0 : hdCost = ceil(log2(moduleNum_)) * delay
606 0 : + static_cast<double>(RHD_FACTOR_TWO * moduleNum_ - RHD_FACTOR_ONE) / moduleNum_ * recvCurSize
607 0 : * userRankSize_ / bandWidth * SECOND2MICROSECOND;
608 : }
609 :
610 : // compare cost among NHR, HD and Ring
611 0 : algType = (hdCost < interMinCost) ? AlgTypeLevel1::ALG_LEVEL1_HD : algType;
612 0 : return HCCL_SUCCESS;
613 : }
614 :
615 : HcclResult
616 0 : CollAlgOperator::SelectAlgoTypeForAllGather(float delay, u64 sendCurSize, float bandWidth, AlgTypeLevel1& algType)
617 : {
618 0 : auto steps = moduleNum_ - 1;
619 : // theoretical time cost of Ring
620 0 : double ringCost
621 0 : = static_cast<double>(steps) * delay
622 0 : + static_cast<double>(steps) / moduleNum_ * sendCurSize * userRankSize_ / bandWidth * SECOND2MICROSECOND;
623 :
624 : // theoretical time cost of NHR
625 0 : double nhrCost = ceil(log2(moduleNum_)) * delay
626 0 : + static_cast<double>(moduleNum_ - 1) / moduleNum_ * sendCurSize * userRankSize_ / bandWidth
627 0 : * SECOND2MICROSECOND;
628 :
629 : // compare costs between NHR and Ring, if same cost, Ring > NHR > HD
630 0 : algType = (nhrCost < ringCost) ? AlgTypeLevel1::ALG_LEVEL1_NHR : AlgTypeLevel1::ALG_LEVEL1_RING;
631 0 : double interMinCost = min(nhrCost, ringCost);
632 :
633 : // theoretical time cost of HD/RHD
634 0 : double hdCost = 0.0;
635 0 : if ((moduleNum_ & (moduleNum_ - 1)) == 0) {
636 : // theoretical time cost of HD
637 : hdCost
638 0 : = log2(moduleNum_) * delay
639 0 : + static_cast<double>(steps) / moduleNum_ * sendCurSize * userRankSize_ / bandWidth * SECOND2MICROSECOND;
640 : } else {
641 : // theoretical time cost of Recursive HD
642 : // the (RHD_FACTOR_TWO * moduleNum_ - RHD_FACTOR_ONE) / moduleNum_ is equal to 1 + (moduleNum_ -1) / moduleNum_
643 0 : hdCost = ceil(log2(moduleNum_)) * delay
644 0 : + static_cast<double>(RHD_FACTOR_TWO * moduleNum_ - RHD_FACTOR_ONE) / moduleNum_ * sendCurSize
645 0 : * userRankSize_ / bandWidth * SECOND2MICROSECOND;
646 : }
647 :
648 : // compare cost among NHR, HD and Ring
649 0 : algType = (hdCost < interMinCost) ? AlgTypeLevel1::ALG_LEVEL1_HD : algType;
650 0 : return HCCL_SUCCESS;
651 : }
652 :
653 : HcclResult
654 0 : CollAlgOperator::SelectAlgoTypeForAllGatherV(float delay, u64 sendCurSize, float bandWidth, AlgTypeLevel1& algType)
655 : {
656 0 : auto steps = moduleNum_ - 1;
657 : // theoretical time cost of Ring
658 0 : double ringCost
659 0 : = static_cast<double>(steps) * delay
660 0 : + static_cast<double>(steps) / moduleNum_ * sendCurSize * userRankSize_ / bandWidth * SECOND2MICROSECOND;
661 :
662 0 : HCCL_DEBUG("[%s] CollAlgOperator for SelectAlgoTypeForAllGatherV", __func__);
663 : // theoretical time cost of NHR
664 0 : double nhrCost = ceil(log2(moduleNum_)) * delay
665 0 : + static_cast<double>(moduleNum_ - 1) / moduleNum_ * sendCurSize * userRankSize_ / bandWidth
666 0 : * SECOND2MICROSECOND;
667 :
668 : // compare costs between NHR and Ring, if same cost, Ring > NHR > HD
669 0 : algType = (nhrCost < ringCost) ? AlgTypeLevel1::ALG_LEVEL1_NHR : AlgTypeLevel1::ALG_LEVEL1_RING;
670 :
671 0 : return HCCL_SUCCESS;
672 : }
673 :
674 : HcclResult
675 0 : CollAlgOperator::SelectAlgoTypeForGather(float delay, u64 sendCurSize, float bandWidth, AlgTypeLevel1& algType)
676 : {
677 0 : auto steps = moduleNum_ - 1;
678 : // theoretical time cost of Ring
679 0 : double ringCost
680 0 : = static_cast<double>(steps) * delay
681 0 : + static_cast<double>(steps) / moduleNum_ * sendCurSize * userRankSize_ / bandWidth * SECOND2MICROSECOND;
682 0 : double hdCost = 0.0;
683 0 : if ((moduleNum_ & (moduleNum_ - 1)) == 0) {
684 : // theoretical time cost of HD
685 : hdCost
686 0 : = log2(moduleNum_) * delay
687 0 : + static_cast<double>(steps) / moduleNum_ * sendCurSize * userRankSize_ / bandWidth * SECOND2MICROSECOND;
688 : } else {
689 : // theoretical time cost of Recursive HD
690 : // the (RHD_FACTOR_TWO * moduleNum_ - RHD_FACTOR_ONE) / moduleNum_ is equal to 1 + (moduleNum_ -1) / moduleNum_
691 0 : hdCost = ceil(log2(moduleNum_)) * delay
692 0 : + static_cast<double>(RHD_FACTOR_TWO * moduleNum_ - RHD_FACTOR_ONE) / moduleNum_ * sendCurSize
693 0 : * userRankSize_ / bandWidth * SECOND2MICROSECOND;
694 : }
695 0 : algType = (hdCost < ringCost) ? AlgTypeLevel1::ALG_LEVEL1_HD : AlgTypeLevel1::ALG_LEVEL1_RING;
696 0 : return HCCL_SUCCESS;
697 : }
698 :
699 : HcclResult
700 0 : CollAlgOperator::SelectAlgoTypeForAllReduce(float delay, u64 curSize, float bandWidth, AlgTypeLevel1& algType)
701 : {
702 0 : auto steps = moduleNum_ - 1;
703 : // theoretical time cost of Ring
704 0 : double ringCost = DOUBLE_SUB_HCCLCMD * static_cast<double>(steps) * delay
705 0 : + DOUBLE_SUB_HCCLCMD * static_cast<double>(steps) / moduleNum_ * curSize
706 0 : / deviceNumPerAggregation_ / bandWidth * SECOND2MICROSECOND;
707 :
708 : // theoretical time cost of NHR
709 0 : double nhrCost = NHR_FACTOR_TWO * ceil(log2(moduleNum_)) * delay
710 0 : + NHR_FACTOR_TWO * static_cast<double>(moduleNum_ - 1) / moduleNum_ * curSize
711 0 : / deviceNumPerAggregation_ / bandWidth * SECOND2MICROSECOND;
712 :
713 : // compare costs between NHR and Ring, if same cost, Ring > NHR > HD
714 0 : algType = (nhrCost < ringCost) ? AlgTypeLevel1::ALG_LEVEL1_NHR : AlgTypeLevel1::ALG_LEVEL1_RING;
715 0 : double interMinCost = min(nhrCost, ringCost);
716 :
717 : // theoretical time cost of HD/RHD
718 0 : double hdCost = 0.0;
719 0 : if ((moduleNum_ & (moduleNum_ - 1)) == 0) {
720 : // theoretical time cost of HD
721 0 : hdCost = DOUBLE_SUB_HCCLCMD * log2(moduleNum_) * delay
722 0 : + DOUBLE_SUB_HCCLCMD * static_cast<double>(steps) / moduleNum_ * curSize / deviceNumPerAggregation_
723 0 : / bandWidth * SECOND2MICROSECOND;
724 : } else {
725 : // theoretical time cost of Recursive HD
726 : // the (RHD_FACTOR_TWO * moduleNum_ - RHD_FACTOR_ONE) / moduleNum_ is equal to 1 + (moduleNum_ -1) / moduleNum_
727 0 : hdCost = DOUBLE_SUB_HCCLCMD * ceil(log2(moduleNum_)) * delay
728 0 : + DOUBLE_SUB_HCCLCMD * static_cast<double>(RHD_FACTOR_TWO * moduleNum_ - RHD_FACTOR_ONE) / moduleNum_
729 0 : * curSize / deviceNumPerAggregation_ / bandWidth * SECOND2MICROSECOND;
730 : }
731 :
732 : // compare cost among NHR, HD and Ring
733 0 : algType = (hdCost < interMinCost) ? AlgTypeLevel1::ALG_LEVEL1_HD : algType;
734 0 : return HCCL_SUCCESS;
735 : }
736 :
737 : HcclResult
738 0 : CollAlgOperator::SelectAlgoTypeForBroadcast(float delay, u64 curSize, float bandWidth, AlgTypeLevel1& algType)
739 : {
740 0 : auto steps = moduleNum_ - 1;
741 : // theoretical time cost of Ring
742 0 : double ringCost = DOUBLE_SUB_HCCLCMD * static_cast<double>(steps) * delay
743 0 : + DOUBLE_SUB_HCCLCMD * static_cast<double>(steps) / moduleNum_ * curSize
744 0 : / deviceNumPerAggregation_ / bandWidth * SECOND2MICROSECOND;
745 0 : double hdCost = 0.0;
746 0 : if ((moduleNum_ & (moduleNum_ - 1)) == 0) {
747 : // theoretical time cost of HD
748 0 : hdCost = DOUBLE_SUB_HCCLCMD * log2(moduleNum_) * delay
749 0 : + DOUBLE_SUB_HCCLCMD * static_cast<double>(steps) / moduleNum_ * curSize / deviceNumPerAggregation_
750 0 : / bandWidth * SECOND2MICROSECOND;
751 : } else {
752 : // theoretical time cost of Recursive HD
753 : // rhd-broadcast = scatter + allgather + copy
754 0 : hdCost = (COPY_TIME_IN_RHD + DOUBLE_SUB_HCCLCMD * floor(log2(moduleNum_))) * delay
755 0 : + (COPY_TIME_IN_RHD + DOUBLE_SUB_HCCLCMD * static_cast<double>(steps) / moduleNum_) * curSize
756 0 : / deviceNumPerAggregation_ / bandWidth * SECOND2MICROSECOND;
757 : }
758 0 : algType = (hdCost < ringCost) ? AlgTypeLevel1::ALG_LEVEL1_HD : AlgTypeLevel1::ALG_LEVEL1_RING;
759 0 : return HCCL_SUCCESS;
760 : }
761 :
762 0 : HcclResult CollAlgOperator::SelectAlgoTypeForReduce(float delay, u64 curSize, float bandWidth, AlgTypeLevel1& algType)
763 : {
764 0 : HCCL_DEBUG("[CollAlgOperator]SelectAlgoTypeForReduce start");
765 0 : auto steps = moduleNum_ - 1;
766 : // theoretical time cost of Ring
767 0 : double ringCost = DOUBLE_SUB_HCCLCMD * static_cast<double>(steps) * delay
768 0 : + DOUBLE_SUB_HCCLCMD * static_cast<double>(steps) / moduleNum_ * curSize
769 0 : / deviceNumPerAggregation_ / bandWidth * SECOND2MICROSECOND;
770 0 : double hdCost = 0.0;
771 0 : if ((moduleNum_ & (moduleNum_ - 1)) == 0) {
772 : // theoretical time cost of HD
773 0 : hdCost = DOUBLE_SUB_HCCLCMD * log2(moduleNum_) * delay
774 0 : + DOUBLE_SUB_HCCLCMD * static_cast<double>(steps) / moduleNum_ * curSize / deviceNumPerAggregation_
775 0 : / bandWidth * SECOND2MICROSECOND;
776 : } else {
777 : // theoretical time cost of Recursive HD
778 : // rhd-broadcast = reducescatter + gather + copy
779 0 : hdCost = (COPY_TIME_IN_RHD + DOUBLE_SUB_HCCLCMD * floor(log2(moduleNum_))) * delay
780 0 : + (COPY_TIME_IN_RHD + DOUBLE_SUB_HCCLCMD * static_cast<double>(steps) / moduleNum_) * curSize
781 0 : / deviceNumPerAggregation_ / bandWidth * SECOND2MICROSECOND;
782 : }
783 0 : algType = (hdCost < ringCost) ? AlgTypeLevel1::ALG_LEVEL1_HD : AlgTypeLevel1::ALG_LEVEL1_RING;
784 0 : return HCCL_SUCCESS;
785 : }
786 :
787 24 : AlgType CollAlgOperator::GetAlgType() { return algType_; }
788 :
789 2 : bool CollAlgOperator::Is2U2PInfer()
790 : {
791 : return (
792 0 : (deviceNumPerAggregation_ == HCCL_DEVICE_NUM_TWO) && (serverNum_ == 1)
793 0 : && (deviceType_ == DevType::DEV_TYPE_910B) && (meshAggregationRankSize_ == HCCL_DEVICE_NUM_TWO)
794 2 : && (pairLinkCounter_[static_cast<u32>(LinkTypeInServer::HCCS_TYPE)] == 0));
795 : }
796 :
797 24 : bool CollAlgOperator::Is910BSingleMesh()
798 : {
799 1 : bool isMeshTopo = topoType_ == TopoType::TOPO_TYPE_NP_MESH || topoType_ == TopoType::TOPO_TYPE_4P_MESH
800 25 : || topoType_ == TopoType::TOPO_TYPE_2P_MESH || topoType_ == TopoType::TOPO_TYPE_1P_MESH;
801 :
802 : bool isSingleMesh
803 24 : = (deviceType_ == DevType::DEV_TYPE_910B) && (isMeshTopo || Is2U2PInfer()) && (userRankSize_ != 1);
804 24 : return isSingleMesh;
805 : }
806 :
807 1 : bool CollAlgOperator::NeedCreateSingleMeshPlane(const bool isInlineReduce)
808 : {
809 : // 910B 图模式非确定计算,inlineReduce使能,MESH拓扑场景下,创建一个mesh平面
810 1 : bool meshSinglePlane = Is910BSingleMesh() && topoMatcher_->GetDeterministicConfig() == DETERMINISTIC_DISABLE
811 1 : && isInlineReduce && (GetWorkflowMode() != HcclWorkflowMode::HCCL_WORKFLOW_MODE_OP_BASE);
812 :
813 1 : return meshSinglePlane;
814 : }
815 :
816 23 : bool CollAlgOperator::SingleMeshInlineReduce(void* inputPtr, void* outputPtr, HcclDataType dataType, HcclReduceOp op)
817 : {
818 23 : bool isInlineReduce = IsSupportSDMAReduce(inputPtr, outputPtr, dataType, op);
819 22 : bool singleMeshInlineReduce = Is910BSingleMesh() && isInlineReduce && isSingleMeshAggregation_;
820 21 : return singleMeshInlineReduce;
821 : }
822 :
823 2 : bool CollAlgOperator::IsMultiMeshInlineReduce(void* inputPtr, void* outputPtr, HcclDataType dataType, HcclReduceOp op)
824 : {
825 0 : bool isMeshTopo = topoType_ == TopoType::TOPO_TYPE_NP_MESH || topoType_ == TopoType::TOPO_TYPE_4P_MESH
826 2 : || topoType_ == TopoType::TOPO_TYPE_2P_MESH || topoType_ == TopoType::TOPO_TYPE_1P_MESH;
827 :
828 2 : bool isInlineReduce = IsSupportSDMAReduce(inputPtr, outputPtr, dataType, op);
829 3 : bool isRdmaReduce = IsSupportRDMAReduce(dataType, op);
830 3 : bool multiMeshInlineReduce = (deviceType_ == DevType::DEV_TYPE_910B) && isMeshTopo && isInlineReduce && isRdmaReduce
831 6 : && (!isSingleMeshAggregation_);
832 3 : return multiMeshInlineReduce;
833 : }
834 :
835 0 : void CollAlgOperator::SetLegacyHcclImpl(std::unique_ptr<hcclImpl>& impl)
836 : {
837 0 : hcclImpl_ = impl.get();
838 0 : return;
839 : }
840 :
841 8 : HcclResult CollAlgOperator::SetRetryEnable(bool retryEnable)
842 : {
843 8 : retryEnable_ = retryEnable;
844 8 : return HCCL_SUCCESS;
845 : }
846 :
847 7 : HcclResult CollAlgOperator::SetAivClearEnable(bool aivClearEnable)
848 : {
849 7 : aivClearEnable_ = aivClearEnable;
850 7 : return HCCL_SUCCESS;
851 : }
852 :
853 0 : HcclResult CollAlgOperator::SetAlgOpContext(AlgOpContext algOpContext)
854 : {
855 0 : algOpContext_ = algOpContext;
856 0 : return HCCL_SUCCESS;
857 : }
858 :
859 29 : bool CollAlgOperator::SupportRetryWithInplaceCheck(
860 : const HcclCMDType& opType, OpParam& param, std::string& algName, u8& isInplaceStatus,
861 : InplaceSupportRetryStatus& inPlaceSupportRetryStatus)
862 : {
863 : // 不支持inplace的通信算子重执行
864 29 : if (IsHcclOpInplace(opType, param, userRank_, userRankSize_, isInplaceStatus)) {
865 13 : void* commInputPtr = nullptr;
866 13 : u64 commInputSize = 0;
867 21 : CHK_RET(cclBufferManager_.GetInCCLbuffer(commInputPtr, commInputSize));
868 13 : if (!FitRetryConditionforInPlaceOp(
869 13 : opType, param, algName, commInputSize, userRankSize_, retryEnable_, inPlaceSupportRetryStatus)) {
870 8 : HCCL_DEBUG(
871 : "[CollAlgOperator][OpRetry][AICPU]hccl aicpu can not retry, opType[%s], inputPtr[%p], "
872 : "outputPtr[%p].",
873 : GetCMDTypeEnumStr(opType).c_str(), param.inputPtr, param.outputPtr);
874 8 : return false;
875 : }
876 : }
877 : // true 存在两种情况:
878 : // 1. 非inplace场景
879 : // 2. 是inplace但同时符合retry条件的场景
880 21 : return true;
881 : }
882 :
883 21 : HcclResult CollAlgOperator::GetNumBlocks(u32& numBlocks)
884 : {
885 21 : CHK_SMART_PTR_NULL(executor_);
886 21 : return executor_->GetNumBlocks(numBlocks);
887 : }
888 :
889 5 : HcclResult CollAlgOperator::SetNumBlocks(const u32& numBlocks)
890 : {
891 5 : CHK_SMART_PTR_NULL(executor_);
892 5 : return executor_->SetNumBlocks(numBlocks);
893 : }
894 :
895 2 : HcclResult CollAlgOperator::GetCache(HcclCacheInfo& cacheInfo)
896 : {
897 2 : CHK_SMART_PTR_NULL(executor_);
898 2 : return executor_->GetCache(cacheInfo);
899 : }
900 :
901 24 : HcclResult CollAlgOperator::SetOpCounter(const OpCounterInfo& opCounter)
902 : {
903 24 : opCounter_ = opCounter;
904 24 : return HCCL_SUCCESS;
905 : }
906 :
907 0 : HcclResult CollAlgOperator::SetRmaInfo(void* rmaInfo)
908 : {
909 0 : CHK_SMART_PTR_NULL(executor_);
910 0 : CHK_PTR_NULL(rmaInfo);
911 0 : return executor_->SetRmaInfo(rmaInfo);
912 : }
913 :
914 9 : HcclResult CollAlgOperator::SelectAlgforAHC(u64 dataSize, AHCOpType ahcOpType)
915 : {
916 9 : if (multiModuleDiffDeviceNumMode_) {
917 0 : return HCCL_SUCCESS;
918 : }
919 :
920 9 : bool isAHCWholeConfig
921 9 : = (algType_.algoLevel0 == AlgTypeLevel0::ALG_LEVEL0_RESERVED
922 9 : && (algType_.algoLevel1 == AlgTypeLevel1::ALG_LEVEL1_AHC
923 0 : || algType_.algoLevel1 == AlgTypeLevel1::ALG_LEVEL1_AHC_BROKE));
924 :
925 9 : CommPlane ahcSubGroupLevel = COMM_LEVEL1_AHC;
926 9 : if (isAHCWholeConfig) {
927 0 : if (deviceType_ != DevType::DEV_TYPE_910_93) {
928 0 : ahcSubGroupLevel = COMM_COMBINE;
929 : } else {
930 0 : ahcSubGroupLevel = COMM_COMBINE_ORDER;
931 : }
932 9 : } else if (deviceType_ != DevType::DEV_TYPE_910_93) {
933 0 : HCCL_DEBUG("[AHCAlgSelect] hccl algorithm: 910B not support level1 ahc, return ERROR.");
934 0 : return HCCL_E_PARA;
935 : }
936 :
937 9 : HCCL_INFO(
938 : "[SelectAlgforAHC] ahcOpType[%u] isAHCWholeConfig[%u] AHClevel[%u] algType_[%u] deviceType_[%u]", ahcOpType,
939 : isAHCWholeConfig, ahcSubGroupLevel, algType_.algoLevel1, deviceType_);
940 :
941 : AlgTypeLevel1 algTypeLevel1;
942 :
943 9 : std::vector<std::vector<std::vector<u32>>> globalSubGroups;
944 9 : std::map<AHCConcOpType, TemplateType> ahcAlgOption;
945 9 : CHK_RET(topoMatcher_->GetGlobalSubGroups(ahcSubGroupLevel, globalSubGroups));
946 9 : topoMatcher_->GetAHCAlgOption(ahcAlgOption);
947 :
948 9 : AHCAlgSelectParam ahcAlgSelectParam;
949 9 : ahcAlgSelectParam.opType = ahcOpType;
950 9 : ahcAlgSelectParam.dataSize = dataSize;
951 :
952 : // AHC 封装算法选择逻辑
953 9 : CHK_RET(AHCAlgSelect(algTypeLevel1, globalSubGroups, ahcAlgOption, ahcAlgSelectParam));
954 :
955 9 : topoMatcher_->SetAHCAlgOption(ahcAlgOption);
956 :
957 9 : auto iter = HCCL_ALGO_LEVEL1_NAME_MAP.find(algTypeLevel1);
958 9 : CHK_PRT_RET(
959 : iter == HCCL_ALGO_LEVEL1_NAME_MAP.end(),
960 : HCCL_ERROR("[AHCAlgSelect] level1: algType_[%u] is invalid.", algTypeLevel1), HCCL_E_INTERNAL);
961 :
962 : // 支持 AHC 自适应调节为 BROKE 类型
963 9 : if (algType_.algoLevel1 != algTypeLevel1 && algType_.algoLevel1 == AlgTypeLevel1::ALG_LEVEL1_AHC) {
964 8 : algType_.algoLevel1 = algTypeLevel1;
965 : }
966 :
967 9 : HCCL_INFO(
968 : "[AHCAlgSelect] hccl algorithm: there are %u server(%u module) in level1, using %s algo", serverNum_,
969 : moduleNum_, iter->second.c_str());
970 :
971 9 : return HCCL_SUCCESS;
972 9 : }
973 :
974 9 : HcclResult CollAlgOperator::AHCAlgSelect(
975 : AlgTypeLevel1& algType, std::vector<std::vector<std::vector<u32>>>& globalSubGroups,
976 : std::map<AHCConcOpType, TemplateType>& ahcAlgOption, AHCAlgSelectParam& ahcAlgSelectParam)
977 : {
978 : // globalSubGroups 参数检查
979 9 : CHK_RET(CommAHCBaseInfo::CheckGlobalGroups(globalSubGroups));
980 :
981 9 : bool isAHCType = false;
982 9 : u32 minSubGroupSize = globalSubGroups[0][0].size();
983 9 : u32 maxSubGroupSize = globalSubGroups[0][0].size();
984 45 : for (u32 i = 1; i < globalSubGroups[0].size(); ++i) {
985 36 : if (globalSubGroups[0][i].size() < minSubGroupSize) {
986 0 : minSubGroupSize = globalSubGroups[0][i].size();
987 : }
988 36 : if (globalSubGroups[0][i].size() > maxSubGroupSize) {
989 0 : maxSubGroupSize = globalSubGroups[0][i].size();
990 : }
991 : }
992 54 : for (u32 i = 0; i < globalSubGroups[0].size(); ++i) {
993 45 : if (globalSubGroups[0][i].size() != minSubGroupSize) {
994 0 : isAHCType = true;
995 0 : break;
996 : }
997 : }
998 :
999 : // 多平面 reduce scatter 和 all gather 算子,强制写死成BROKE类型
1000 9 : if (deviceNumPerServer_ != 1 && ahcAlgSelectParam.opType != AHCOpType::AHC_OP_TYPE_ALLREDUCE) {
1001 0 : isAHCType = false;
1002 : }
1003 :
1004 : // add AHC Conc Type logic here, modify init Type depend on the input para
1005 9 : CHK_RET(AHCAlgOptionSelect(algType, globalSubGroups, ahcAlgOption, ahcAlgSelectParam));
1006 :
1007 9 : if (ahcAlgSelectParam.enableAlgAutoSelect == false) { // 关闭算法自适应功能时,默认设置AHC算法
1008 0 : algType = AlgTypeLevel1::ALG_LEVEL1_AHC;
1009 0 : return HCCL_SUCCESS;
1010 : }
1011 :
1012 9 : if (isAHCType) {
1013 0 : algType = AlgTypeLevel1::ALG_LEVEL1_AHC; // 设置为 AHC 类型
1014 : } else {
1015 9 : algType = AlgTypeLevel1::ALG_LEVEL1_AHC_BROKE; // 设置为 BROKE 类型
1016 : }
1017 :
1018 9 : HCCL_DEBUG(
1019 : "[AHCAlgSelect] end minSubGroupSize = %u maxSubGroupSize = %u isAHCType = %u", minSubGroupSize, maxSubGroupSize,
1020 : isAHCType);
1021 :
1022 9 : return HCCL_SUCCESS;
1023 : }
1024 :
1025 11 : HcclResult CollAlgOperator::AHCAlgOptionSelect(
1026 : const AlgTypeLevel1& algType, std::vector<std::vector<std::vector<u32>>>& globalSubGroups,
1027 : std::map<AHCConcOpType, TemplateType>& ahcAlgOption, const AHCAlgSelectParam& ahcAlgSelectParam)
1028 : {
1029 : (void)algType;
1030 : (void)ahcAlgSelectParam;
1031 11 : AHCConcOpType ahcConcOpType;
1032 : // 一层组间拼接时,分组数大于设定阈值则修改默认算法为NHR
1033 11 : if (globalSubGroups[0].size() <= AHC_LEVEL0_GROUP_SIZE_THRESHOLD) {
1034 4 : HCCL_DEBUG("[AHCAlgSelect] conc inter select type RING ");
1035 4 : ahcConcOpType = {AHCLevel::AHC_LEVEL_0, ConcType::CONC_INTER, AHCOpType::AHC_OP_TYPE_REDUCE_SCATTER};
1036 4 : ahcAlgOption[ahcConcOpType] = TemplateType::TEMPLATE_REDUCESCATTER_RING;
1037 :
1038 4 : ahcConcOpType = {AHCLevel::AHC_LEVEL_0, ConcType::CONC_INTER, AHCOpType::AHC_OP_TYPE_ALLREDUCE};
1039 4 : ahcAlgOption[ahcConcOpType] = TemplateType::TEMPLATE_ALL_REDUCE_RING;
1040 :
1041 4 : ahcConcOpType = {AHCLevel::AHC_LEVEL_0, ConcType::CONC_INTER, AHCOpType::AHC_OP_TYPE_ALLGATHER};
1042 4 : ahcAlgOption[ahcConcOpType] = TemplateType::TEMPLATE_ALL_GATHER_RING;
1043 : } else {
1044 7 : HCCL_DEBUG("[AHCAlgSelect] conc inter select type NHR ");
1045 7 : ahcConcOpType = {AHCLevel::AHC_LEVEL_0, ConcType::CONC_INTER, AHCOpType::AHC_OP_TYPE_REDUCE_SCATTER};
1046 7 : ahcAlgOption[ahcConcOpType] = TemplateType::TEMPLATE_REDUCESCATTER_NHR;
1047 :
1048 7 : ahcConcOpType = {AHCLevel::AHC_LEVEL_0, ConcType::CONC_INTER, AHCOpType::AHC_OP_TYPE_ALLREDUCE};
1049 7 : ahcAlgOption[ahcConcOpType] = TemplateType::TEMPLATE_ALL_REDUCE_NHR;
1050 :
1051 7 : ahcConcOpType = {AHCLevel::AHC_LEVEL_0, ConcType::CONC_INTER, AHCOpType::AHC_OP_TYPE_ALLGATHER};
1052 7 : ahcAlgOption[ahcConcOpType] = TemplateType::TEMPLATE_ALL_GATHER_NHR;
1053 : }
1054 11 : return HCCL_SUCCESS;
1055 : }
1056 :
1057 0 : u32 CollAlgOperator::CalcOptimalIntraRingsize(u64 count, HcclDataType dataType, HcclCMDType opType)
1058 : {
1059 0 : if (!topoMatcher_->GetARSFlag())
1060 0 : return 0;
1061 :
1062 0 : u32 level0RankSize = topoMatcher_->GetCommPlaneRanks(COMM_LEVEL0)[0].size();
1063 0 : u32 rankSizeInSuperPod = topoMatcher_->GetCommPlaneRanks(COMM_ARS)[0].size();
1064 0 : u32 perDataSize = 0;
1065 0 : CHK_RET(SalGetDataTypeSize(dataType, perDataSize));
1066 : // 不支持 ARS 或环内卡数不是 2 的倍数
1067 0 : u32 level0RingSize = 1;
1068 0 : if (!isARSDoubleRing_ || (level0RankSize % FACTOR_TWO != 0)) {
1069 0 : HCCL_INFO(
1070 : "not Support ARS doubleRing, level0RingSize:[%u], level0RankSize[%u].", level0RingSize, level0RankSize);
1071 0 : return level0RingSize;
1072 : }
1073 : // --- 1. 带宽 & 基本参数 ---
1074 : float bwHCCS, bwHBM, bwSIO;
1075 0 : constexpr u32 level0 = 0;
1076 0 : constexpr u32 level2 = 2;
1077 0 : constexpr u32 level3 = 3;
1078 0 : CHK_RET(GetBandWidthPerNPU(level0, userRankSize_, deviceNumPerAggregation_, bwHCCS));
1079 0 : CHK_RET(GetBandWidthPerNPU(level2, userRankSize_, deviceNumPerAggregation_, bwHBM));
1080 0 : CHK_RET(GetBandWidthPerNPU(level3, userRankSize_, deviceNumPerAggregation_, bwSIO));
1081 0 : float latency = BASE_COMM_LATENCY / MULTIPLIER_MS2US; // ms
1082 : // --- 2. 数据总量 (GB) ---
1083 0 : float baseSizeGB = static_cast<double>(count) * perDataSize / GB2B;
1084 0 : float totalSize = baseSizeGB;
1085 0 : HCCL_INFO(
1086 : "CalcOptimalIntraRingsize: count[%u], totalSize:[%lf]GB, perDataSize[%u].", count, totalSize, perDataSize);
1087 0 : if (opType == HcclCMDType::HCCL_CMD_REDUCE_SCATTER || opType == HcclCMDType::HCCL_CMD_ALLGATHER) {
1088 0 : totalSize *= rankSizeInSuperPod;
1089 : }
1090 : // --- 3. 枚举可能的环大小 ---
1091 0 : std::vector<u32> factors;
1092 0 : for (u32 i = 1; i <= rankSizeInSuperPod / i; ++i) {
1093 0 : if (rankSizeInSuperPod % i == 0) {
1094 0 : if (i != rankSizeInSuperPod / i) {
1095 0 : factors.push_back(rankSizeInSuperPod / i);
1096 : }
1097 0 : factors.push_back(i);
1098 : }
1099 : }
1100 0 : std::sort(factors.begin(), factors.end());
1101 : // --- 4. 计算最优带宽 ---
1102 0 : double maxBwARS = 0.0;
1103 0 : for (u32 N1 : factors) {
1104 : // 传输时延 (ms)
1105 : double latencyIntra;
1106 0 : if (N1 == FACTOR_TWO) {
1107 0 : latencyIntra = totalSize * MULTIPLIER_S2MS / FACTOR_TWO / bwSIO;
1108 0 : } else if ((N1 % FACTOR_TWO == 0) && (N1 > FACTOR_TWO)) {
1109 0 : latencyIntra = (N1 - 1) * totalSize * MULTIPLIER_S2MS / N1 / bwHCCS / FACTOR_TWO;
1110 : } else {
1111 0 : latencyIntra = (N1 - 1) * totalSize * MULTIPLIER_S2MS / N1 / bwHCCS;
1112 : }
1113 0 : u32 N2 = rankSizeInSuperPod / N1;
1114 : // 静态时延 (ms)
1115 0 : double interStep = (algType_.algoLevel1 == AlgTypeLevel1::ALG_LEVEL1_RING) ? (N2 - 1) : log2(N2);
1116 0 : double latencyStep = (interStep + (N1 - 1)) * latency;
1117 0 : double latencyInter = (N2 - 1) * totalSize * MULTIPLIER_S2MS / N1 / N2 / bwHCCS;
1118 : // HBM 拷贝时延 (ms)
1119 0 : double latencyCopy = totalSize * MULTIPLIER_S2MS / bwHBM;
1120 0 : u8 mul = (HcclCMDType::HCCL_CMD_ALLREDUCE == opType) ? FACTOR_TWO : 1;
1121 0 : double timeCost = mul * (latencyStep + latencyIntra + latencyInter) + latencyCopy;
1122 0 : double bwARS = totalSize / timeCost; // GB/ms
1123 0 : if (bwARS > maxBwARS) {
1124 0 : level0RingSize = N1;
1125 0 : maxBwARS = bwARS;
1126 : }
1127 : }
1128 0 : HCCL_INFO("level0RingSize:[%u], level0RankSize[%u], totalSize:[%lf]GB", level0RingSize, level0RankSize, totalSize);
1129 0 : return level0RingSize;
1130 0 : }
1131 :
1132 20 : bool CollAlgOperator::IsNeedStrictMode(const OpParam& param)
1133 : {
1134 : bool isStrictMode
1135 20 : = (topoMatcher_->GetDeterministicConfig() == DETERMINISTIC_STRICT)
1136 0 : && (param.DataDes.dataType == HCCL_DATA_TYPE_FP16 || param.DataDes.dataType == HCCL_DATA_TYPE_FP32
1137 0 : || param.DataDes.dataType == HCCL_DATA_TYPE_BFP16 || param.DataDes.dataType == HCCL_DATA_TYPE_FP64)
1138 0 : && (param.reduceType == HCCL_REDUCE_SUM || param.reduceType == HCCL_REDUCE_PROD)
1139 20 : && userRankSize_ >= MIN_STRICT_RANK_NUM;
1140 :
1141 20 : return isStrictMode;
1142 : }
1143 :
1144 0 : bool CollAlgOperator::CheckStrictCondition(const OpParam& param) const
1145 : {
1146 0 : CHK_PRT_RET(
1147 : multiModuleDiffDeviceNumMode_ || multiSuperPodDiffDeviceNumMode_ || multiSuperPodDiffServerNumMode_,
1148 : HCCL_ERROR("[CollAlgOperator][CheckStrictCondition] DETERMINISTIC_STRICT mode not support asymmetrical topo."),
1149 : false);
1150 :
1151 0 : CHK_PRT_RET(
1152 : param.reduceType == HCCL_REDUCE_PROD,
1153 : HCCL_ERROR("[CollAlgOperator][CheckStrictCondition] DETERMINISTIC_STRICT mode not support PROD."), false);
1154 :
1155 0 : CHK_PRT_RET(
1156 : param.DataDes.dataType == HCCL_DATA_TYPE_FP64,
1157 : HCCL_ERROR("[CollAlgOperator][CheckStrictCondition] DETERMINISTIC_STRICT mode not support FP64."), false);
1158 :
1159 0 : CHK_PRT_RET(
1160 : GetExternalInputInterHccsDisable(),
1161 : HCCL_ERROR("[CollAlgOperator][CheckStrictCondition] DETERMINISTIC_STRICT mode not support HCCS disable."),
1162 : false);
1163 :
1164 0 : return true;
1165 : }
1166 :
1167 : } // namespace hccl
|