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 <math.h>
12 : #include "alg_template_register.h"
13 : #include "all_reduce_doubling.h"
14 :
15 : namespace hccl {
16 :
17 : // Doubling算法实现AllReduce,只用于server内通信
18 0 : AllReduceDoubling::AllReduceDoubling(const HcclDispatcher dispatcher) : AlgTemplateBase(dispatcher) {}
19 :
20 0 : AllReduceDoubling::~AllReduceDoubling() {}
21 :
22 0 : HcclResult AllReduceDoubling::Prepare(u64 reduceAttrBitMap, [[maybe_unused]] HcomCollOpInfo* opInfo)
23 : {
24 0 : reduceAttr_ = reduceAttrBitMap;
25 0 : return HCCL_SUCCESS;
26 : }
27 :
28 0 : HcclResult AllReduceDoubling::RunAsync(const u32 rank, const u32 rankSize, const std::vector<LINK>& links)
29 : {
30 0 : HCCL_INFO(
31 : "[AllReduceDoubling] runAsync rank[%u] ranksize[%u] inputMem[%p] outputMem[%p] count[%llu]", rank, rankSize,
32 : inputMem_.ptr(), outputMem_.ptr(), count_);
33 :
34 : // 基本的检查
35 0 : CHK_RET(SimpleCheck(rank, rankSize, links));
36 :
37 : // 判断rank_size == 1
38 0 : if (rankSize == 1) {
39 : // 对于Doubling,input和output必须是两块不同的内存
40 0 : CHK_RET(HcclD2DMemcpyAsync(dispatcher_, outputMem_, inputMem_, stream_));
41 0 : return HCCL_SUCCESS;
42 : }
43 :
44 : // 设置Slices
45 0 : if (slices_.size() != 0) {
46 0 : HCCL_WARNING("[AllReduceDoubling] slices_ will be not used in executor.");
47 : }
48 :
49 : // 执行算法
50 0 : CHK_RET(RunAllReduce(rank, rankSize, links));
51 :
52 0 : HCCL_INFO("AllReduceDoubling finished: rank[%u] ranksize[%u]", rank, rankSize);
53 0 : return HCCL_SUCCESS;
54 : }
55 :
56 0 : HcclResult AllReduceDoubling::SimpleCheck(const u32 rank, const u32 rankSize, const std::vector<LINK>& links)
57 : {
58 : // 判断stream, dispatcher是否为空
59 0 : CHK_SMART_PTR_NULL(dispatcher_);
60 0 : CHK_PTR_NULL(stream_.ptr());
61 :
62 : // 判断Memory是否为空
63 0 : CHK_PRT_RET(!inputMem_, HCCL_ERROR("[AllReduceDoubling] rank[%u] inputmem is null", rank), HCCL_E_PTR);
64 0 : CHK_PRT_RET(!outputMem_, HCCL_ERROR("[AllReduceDoubling] rank[%u] outputmem is null", rank), HCCL_E_PTR);
65 :
66 : // 必须有两块memory
67 0 : CHK_PRT_RET(
68 : inputMem_ == outputMem_,
69 : HCCL_ERROR("[AllReduceDoubling] rank[%u] inputMem and outputMem should be different", rank), HCCL_E_PARA);
70 :
71 : // 判断links数量是否正确
72 0 : CHK_PRT_RET(
73 : links.size() < rankSize,
74 : HCCL_ERROR(
75 : "[AllReduceDoubling] rank[%u] link size[%llu] is less than "
76 : "rank size[%u]",
77 : rank, links.size(), rankSize),
78 : HCCL_E_PARA);
79 :
80 : // 判断rankSize是否为2的幂次
81 0 : CHK_PRT_RET(
82 : (rankSize & (rankSize - 1)) != 0,
83 : HCCL_ERROR(
84 : "[AllReduceDoubling] rankSize must be power of 2, "
85 : "but get rankSize=%u",
86 : rankSize),
87 : HCCL_E_PARA);
88 0 : return HCCL_SUCCESS;
89 : }
90 :
91 0 : HcclResult AllReduceDoubling::RunAllReduce(const u32 rank, const u32 rankSize, const std::vector<LINK>& links)
92 : {
93 0 : u64 totalSize = count_ * SIZE_TABLE[dataType_];
94 0 : DeviceMem cclInMem = inputMem_.range(0, totalSize);
95 0 : DeviceMem cclOutMem = outputMem_.range(0, totalSize);
96 :
97 0 : u32 nSteps = static_cast<u32>(log2(rankSize));
98 0 : for (u32 step = 0; step < nSteps; step++) {
99 : // 计算邻居并获取link
100 0 : u32 neighbor = rank ^ (1 << step);
101 0 : const LINK& link = links[neighbor];
102 0 : CHK_PTR_NULL(link);
103 :
104 : // 拷贝数据,避免读写冲突
105 0 : if (step == 0) { // 把本端的cclIn拷到cclOut(cclIn是整个template的入口)
106 0 : CHK_RET(HcclD2DMemcpyAsync(dispatcher_, cclOutMem, cclInMem, stream_));
107 : } else { // 把本端的cclOut拷到cclIn(cclOut是上一步的结果)
108 0 : CHK_RET(HcclD2DMemcpyAsync(dispatcher_, cclInMem, cclOutMem, stream_));
109 : }
110 :
111 : // Ack
112 0 : CHK_RET(link->TxAck(stream_));
113 0 : CHK_RET(link->RxAck(stream_));
114 :
115 : // 从对端的cclIn读到本端的cclOut
116 0 : void* remMemPtr = nullptr;
117 0 : CHK_RET(link->GetRemoteMem(UserMemType::INPUT_MEM, &remMemPtr));
118 0 : DeviceMem remoteCclInMem = DeviceMem::create(static_cast<u8*>(remMemPtr), totalSize);
119 0 : CHK_RET(HcclReduceAsync(
120 : dispatcher_, remoteCclInMem.ptr(), count_, dataType_, reductionOp_, stream_, cclOutMem.ptr(),
121 : link->GetRemoteRank(), link->GetLinkType(), INLINE_REDUCE_BIT));
122 :
123 : // DataSignal
124 0 : CHK_RET(link->TxDataSignal(stream_));
125 0 : CHK_RET(link->RxDataSignal(stream_));
126 0 : }
127 0 : return HCCL_SUCCESS;
128 0 : }
129 : REGISTER_TEMPLATE(TemplateType::TEMPLATE_ALL_REDUCE_DOUBLING, AllReduceDoubling);
130 : } // namespace hccl
|