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