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 "ccu_res_allocator.h"
12 :
13 : #include <algorithm>
14 : #include <climits>
15 :
16 : #include "ccu_res_specs.h"
17 :
18 : namespace hcomm {
19 :
20 1778 : HcclResult CcuResIdAllocator::Alloc(const uint32_t num, const bool consecutive,
21 : std::vector<ResInfo> &allocatedResInfos, const std::string &dfxInfo)
22 : {
23 1778 : CHK_PRT_RET(num == 0,
24 : HCCL_ERROR("[CcuResIdAllocator][%s] failed, request num is 0.", __func__),
25 : HcclResult::HCCL_E_PARA);
26 :
27 1777 : std::unique_lock<std::mutex> lock(innerMutex_);
28 : // 快速判断是否可以分配
29 1777 : const uint32_t freeSize = capacity_ - allocatedSize_;
30 1777 : if (num > freeSize) {
31 2 : HCCL_WARNING("[CcuResIdAllocator][%s] failed, resType[%s], requested num[%u] exceeds "
32 : "currently free size[%u].",
33 : __func__, dfxInfo.c_str(), num, freeSize);
34 2 : HCCL_RUN_INFO("Insufficient CCU Resource: %s, requestNum[%u], freeNum[%u].", dfxInfo.c_str(), num, freeSize);
35 2 : return HcclResult::HCCL_E_UNAVAIL;
36 : }
37 :
38 1775 : std::vector<ResInfo> newResInfos;
39 1775 : uint32_t leftNum = num;
40 1775 : uint32_t tryStartId = 0;
41 : // 对于要求连续的资源需要提供一个足够大小的空闲块
42 : // 对于非连续需要每次不为 0
43 1775 : const uint32_t limitSize = consecutive ? num - 1 : 0;
44 : // 顺序优先分配,遍历已分配的连续块,寻找当前块与下个块之间是否有足够大小空间
45 1775 : resInfos_.emplace_back(capacity_, 0); // 临时添加一个尾资源,简化判断逻辑
46 2269 : for (size_t i = 0; i < resInfos_.size(); i++) {
47 2268 : const auto &resInfo = resInfos_[i];
48 2268 : uint32_t partNum = std::min(resInfo.startId - tryStartId, leftNum);
49 2268 : if (partNum > limitSize) {
50 1774 : newResInfos.emplace_back(tryStartId, partNum); // 该空闲块足够大,分配
51 1774 : leftNum -= partNum;
52 1774 : if (leftNum == 0) {
53 1774 : break;
54 : }
55 : }
56 494 : tryStartId = resInfo.startId + resInfo.num; // 更新当前块起始位置
57 : }
58 1775 : resInfos_.pop_back(); // 删除临时添加的尾资源
59 : // 只有连续要求的资源才可能剩余,此时分配失败,新块为空
60 1775 : if (leftNum != 0) {
61 1 : HCCL_WARNING("[CcuResIdAllocator][%s] failed, no enough consecutive free "
62 : "resource ids for requested num[%u].", __func__, num);
63 1 : HCCL_RUN_INFO("Insufficient CCU Resource: consecutived %s, requestNum[%u], freeNum[%u].",
64 : dfxInfo.c_str(), num, freeSize);
65 1 : return HcclResult::HCCL_E_UNAVAIL;
66 : }
67 :
68 1774 : allocatedSize_ += num;
69 1774 : AllocResInfo(newResInfos); // 将分配的所有资源记录
70 1774 : allocatedResInfos = newResInfos;
71 1774 : return HcclResult::HCCL_SUCCESS;
72 1777 : }
73 :
74 1774 : void CcuResIdAllocator::AllocResInfo(std::vector<ResInfo> newResInfos)
75 : {
76 1774 : if (resInfos_.empty()) { // 首次分配直接添加块
77 1281 : resInfos_.emplace_back(newResInfos.front());
78 1281 : return;
79 : }
80 : // 内部变量始终维护最简的连续块,对需要合并的块更新
81 493 : size_t newIdx = 0;
82 493 : size_t idx = 0;
83 986 : while (newIdx < newResInfos.size() && idx < resInfos_.size()) {
84 493 : auto &newResInfo = newResInfos[newIdx];
85 493 : auto &resInfo = resInfos_[idx];
86 : // 跳过无关的资源块,使得resInfo是newResInfo的后续块
87 493 : if (newResInfo.startId >= resInfo.startId) {
88 492 : idx++;
89 492 : continue;
90 : }
91 : // 检查当前块是否与后续块连续,如果连续则合并
92 1 : if (newResInfo.startId == resInfo.startId - newResInfo.num) {
93 1 : newResInfo.num += resInfo.num;
94 1 : resInfos_.erase(resInfos_.begin() + idx);
95 : }
96 : // 如果当前块是首块则插入首块
97 1 : if (idx == 0) {
98 1 : resInfos_.insert(resInfos_.begin(), newResInfo);
99 1 : newIdx++;
100 1 : continue;
101 : }
102 : // 分配保证如果当前块不是首块则一定与前一个块连续,更新前一个块
103 0 : resInfos_[idx - 1].num += newResInfo.num;
104 0 : newIdx++;
105 : }
106 : // 如果有剩余块一定与最后一个块连续,更新最后一个块
107 493 : if (newIdx < newResInfos.size()) {
108 492 : resInfos_.back().num += newResInfos.back().num;
109 : }
110 : }
111 :
112 104 : static HcclResult CheckReleasePara(const uint32_t startId, const uint32_t num,
113 : const uint32_t capacity)
114 : {
115 104 : CHK_PRT_RET(num == 0,
116 : HCCL_ERROR("[CcuResIdAllocator][%s] failed, resource num is 0.", __func__),
117 : HcclResult::HCCL_E_PARA);
118 :
119 103 : CHK_PRT_RET(num > capacity,
120 : HCCL_ERROR("[CcuResIdAllocator][%s] failed, resource num[%u] "
121 : "is greater than capacity[%u]", __func__, num, capacity),
122 : HcclResult::HCCL_E_PARA);
123 :
124 102 : CHK_PRT_RET(startId > capacity - num,
125 : HCCL_ERROR("[CcuResIdAllocator][%s] failed, resource startId[%u] "
126 : "num[%u] capacity[%u]", __func__, startId, num, capacity),
127 : HcclResult::HCCL_E_PARA);
128 :
129 101 : return HcclResult::HCCL_SUCCESS;
130 : }
131 :
132 104 : HcclResult CcuResIdAllocator::Release(const uint32_t startId, const uint32_t num)
133 : {
134 104 : CHK_RET(CheckReleasePara(startId, num, capacity_));
135 :
136 101 : std::unique_lock<std::mutex> lock(innerMutex_);
137 :
138 : // 找到需要释放的资源块
139 101 : const size_t resIndex = FindReleaseResIndex(startId);
140 101 : CHK_PRT_RET(resIndex >= resInfos_.size(),
141 : HCCL_ERROR("[CcuResIdAllocator][%s] failed, resource startId[%u] num[%u] "
142 : "has not been allocated yet. ", __func__, startId, num),
143 : HcclResult::HCCL_E_PARA);
144 :
145 : // 判断申请释放的资源是否越界
146 100 : const auto &resInfo = resInfos_[resIndex];
147 100 : uint32_t allocatedNum = resInfo.startId + resInfo.num - startId;
148 100 : CHK_PRT_RET(num > allocatedNum,
149 : HCCL_ERROR("[CcuResIdAllocator][%s] failed, resource num[%u] is greater "
150 : "than the allocated num[%u].", __func__, num, allocatedNum),
151 : HcclResult::HCCL_E_PARA);
152 :
153 : // 将资源块释放并更新
154 99 : ReleaseResInfo(resIndex, startId, num);
155 99 : return HcclResult::HCCL_SUCCESS;
156 101 : }
157 :
158 101 : size_t CcuResIdAllocator::FindReleaseResIndex(const uint32_t startId) const
159 : {
160 101 : size_t resIndex = 0;
161 101 : const size_t maxIndex = resInfos_.size();
162 101 : while (resIndex < maxIndex) {
163 100 : const auto &resInfo = resInfos_[resIndex];
164 100 : if (startId >= resInfo.startId + resInfo.num) {
165 0 : resIndex++;
166 0 : continue;
167 : }
168 100 : if (startId >= resInfo.startId) {
169 100 : break; // 资源id属于该资源块
170 : }
171 0 : return maxIndex; // 无法找到已分配资源块,返回错误索引
172 : }
173 101 : return resIndex;
174 : }
175 :
176 99 : void CcuResIdAllocator::ReleaseResInfo(const size_t resIndex,
177 : const uint32_t startId, const uint32_t num)
178 : {
179 99 : allocatedSize_ -= num;
180 :
181 99 : auto &resInfo = resInfos_[resIndex];
182 : // 释放的资源在资源块起始部分
183 99 : if (startId == resInfo.startId) {
184 : // 恰好是整块资源,则全部释放
185 41 : if (num == resInfo.num) {
186 38 : resInfos_.erase(resInfos_.begin() + resIndex);
187 98 : return;
188 : }
189 : // 非整块资源则更新资源块起始位置和大小
190 3 : resInfo.startId += num;
191 3 : resInfo.num -= num;
192 3 : return;
193 : }
194 :
195 58 : uint32_t leftNum = startId - resInfo.startId;
196 58 : uint32_t rightNum = resInfo.num - leftNum - num;
197 : // 释放的资源在资源块末尾,更新资源块大小
198 58 : if (rightNum == 0) {
199 57 : resInfo.num -= num;
200 57 : return;
201 : }
202 : // 释放的资源在资源块中间,拆分为两个资源块
203 1 : resInfo.num = leftNum; // 左部分块更新数据
204 : // 右部分块需要新增
205 1 : resInfos_.emplace(resInfos_.begin() + resIndex + 1, startId + num, rightNum);
206 : }
207 :
208 180 : HcclResult CcuResAllocator::Init()
209 : {
210 180 : auto& ccuResSpecs = CcuResSpecifications::GetInstance(devLogicId_);
211 : // 获取静态定义的资源规格查询函数列表,遍历构造
212 180 : uint32_t capacity = 0;
213 1620 : for (const auto &pair : CcuResSpecifications::GET_RES_SPEC_FUNC_ARRAY) {
214 1440 : const ResType resType = pair.first;
215 1440 : const CcuResSpecifications::GetResSpecFunc getFunc = pair.second;
216 1440 : (void)(ccuResSpecs.*getFunc)(dieId_, capacity); // 获取失败时容量为 0,后续分配按资源不足处理
217 1440 : std::unique_ptr<CcuResIdAllocator> allocatorPtr = nullptr;
218 1440 : HCCL_RUN_INFO("[CcuResAllocator][%s] resType[%s], capacity[%u]",
219 : __func__, resType.Describe().c_str(), capacity);
220 1440 : allocatorPtr.reset((new (std::nothrow) CcuResIdAllocator(capacity)));
221 1440 : CHK_PTR_NULL(allocatorPtr);
222 1440 : idAllocatorMap_[static_cast<uint8_t>(resType)] = std::move(allocatorPtr);
223 1440 : }
224 :
225 180 : (void)ccuResSpecs.GetXnNum(dieId_, xnSpecNum_);
226 180 : (void)ccuResSpecs.GetCountXnNum(dieId_, countXnSpecNum_);
227 180 : return HcclResult::HCCL_SUCCESS;
228 : }
229 :
230 1447 : HcclResult CcuResAllocator::Alloc(const ResType resType, const uint32_t num,
231 : const bool consecutive, std::vector<ResInfo> &resInfos)
232 : {
233 1447 : auto resTypeIter = idAllocatorMap_.find(static_cast<uint8_t>(resType));
234 1447 : if (resTypeIter == idAllocatorMap_.end()) {
235 1 : HCCL_ERROR("[CcuResAllocator][%s] failed, invalid resource type[%s].",
236 : __func__, resType.Describe().c_str());
237 1 : return HcclResult::HCCL_E_PARA;
238 : }
239 1446 : return resTypeIter->second->Alloc(num, consecutive, resInfos, resType.Describe());
240 : }
241 :
242 66 : HcclResult CcuResAllocator::Release(const ResType resType, const uint32_t startId,
243 : const uint32_t num)
244 : {
245 66 : auto resTypeIter = idAllocatorMap_.find(static_cast<uint8_t>(resType));
246 66 : if (resTypeIter == idAllocatorMap_.end()) {
247 1 : HCCL_ERROR("[CcuResAllocator][%s] failed, invalid resource type[%s].",
248 : __func__, resType.Describe().c_str());
249 1 : return HcclResult::HCCL_E_PARA;
250 : }
251 65 : return resTypeIter->second->Release(startId, num);
252 : }
253 :
254 2 : uint32_t CcuResIdAllocator::GetConsecutiveRemainSize() const
255 : {
256 : // 扫描 resInfos_ 找最大连续空闲区 (类似 QueryRemainRes 的 gap scan)
257 2 : uint32_t cursor = 0;
258 2 : uint32_t maxGap = 0;
259 5 : for (const auto &r : resInfos_) {
260 3 : if (r.startId > cursor) {
261 1 : maxGap = std::max(maxGap, r.startId - cursor);
262 : }
263 3 : cursor = r.startId + r.num;
264 : }
265 2 : if (capacity_ > cursor) {
266 1 : maxGap = std::max(maxGap, capacity_ - cursor);
267 : }
268 2 : return maxGap;
269 : }
270 :
271 1 : std::string CcuResIdAllocator::Describe() const
272 : {
273 : return Hccl::StringFormat("CcuResIdAllocator[capacity=%u, allocatedSize=%u, "
274 1 : "resInfos_size=%u]", capacity_, allocatedSize_, resInfos_.size());
275 : }
276 :
277 0 : uint32_t CcuResAllocator::GetConsecutiveRemainSize(const ResType resType) const
278 : {
279 0 : auto it = idAllocatorMap_.find(static_cast<uint8_t>(resType));
280 0 : if (it == idAllocatorMap_.end()) return 0;
281 0 : return it->second->GetConsecutiveRemainSize();
282 : }
283 :
284 1 : std::string CcuResAllocator::Describe() const
285 : {
286 : return Hccl::StringFormat("CcuResAllocator[devLogicId=%u, dieId=%u, "
287 1 : "idAllocatorSize=[%u]]", devLogicId_, dieId_, idAllocatorMap_.size());
288 : }
289 :
290 3 : HcclResult CcuResAllocator::AllocCountXn(const uint32_t num, ResInfo &resInfo)
291 : {
292 3 : constexpr ResType resType = ResType::COUNT_XN;
293 3 : auto resTypeIter = idAllocatorMap_.find(static_cast<uint8_t>(resType));
294 3 : if (resTypeIter == idAllocatorMap_.end()) {
295 0 : HCCL_ERROR("[CcuResAllocator][%s] failed, invalid resource type[%s].",
296 : __func__, resType.Describe().c_str());
297 0 : return HcclResult::HCCL_E_PARA;
298 : }
299 :
300 3 : std::vector<ResInfo> resInfos;
301 6 : auto ret = resTypeIter->second->Alloc(num, true, resInfos);
302 3 : if (ret == HcclResult::HCCL_E_UNAVAIL) {
303 1 : HCCL_WARNING("[CcuResAllocator][%s] failed, count xn resources are unavailable, ",
304 : "retry to allocate with normal xn resources, num[%u], devLogicId[%d].",
305 : __func__, num, devLogicId_);
306 1 : ret = Alloc(ResType::XN, num, true, resInfos);
307 1 : if (ret == HcclResult::HCCL_SUCCESS) {
308 1 : resInfo = resInfos[0]; // 连续分配成功时,一定只有一个元素
309 : }
310 1 : return ret;
311 : }
312 2 : CHK_RET(ret);
313 : // CountXn编号切换为全局Xn编号
314 2 : resInfo.startId = resInfos[0].startId + xnSpecNum_;
315 2 : resInfo.num = resInfos[0].num;
316 2 : return HcclResult::HCCL_SUCCESS;
317 3 : }
318 :
319 2 : HcclResult CcuResAllocator::ReleaseCountXn(const uint32_t startId, const uint32_t num)
320 : {
321 2 : if (startId < xnSpecNum_) { // 未超过Xn规格则为普通Xn
322 1 : return Release(ResType::XN, startId, num);
323 : }
324 :
325 1 : constexpr ResType resType = ResType::COUNT_XN;
326 1 : auto resTypeIter = idAllocatorMap_.find(static_cast<uint8_t>(resType));
327 1 : if (resTypeIter == idAllocatorMap_.end()) {
328 0 : HCCL_ERROR("[CcuResAllocator][%s] failed, invalid resource type[%s].",
329 : __func__, resType.Describe().c_str());
330 0 : return HcclResult::HCCL_E_PARA;
331 : }
332 1 : return resTypeIter->second->Release(startId - xnSpecNum_, num);
333 : }
334 : } // namespace hcomm
|