1 /**
2 * Copyright 2019 Huawei Technologies Co., Ltd
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include "minddata/dataset/util/buddy.h"
17 #include <iomanip>
18 #include <stdexcept>
19
20 #include "minddata/dataset/util/memory_pool.h"
21 #include "minddata/dataset/util/log_adapter.h"
22
BitLeftShift(uint64_t v,uint64_t n)23 inline uint64_t BitLeftShift(uint64_t v, uint64_t n) { return (v << n); }
24
BitRightShift(uint64_t v,uint64_t n)25 inline uint64_t BitRightShift(uint64_t v, uint64_t n) { return (v >> n); }
26
BitOr(uint64_t rhs,uint64_t lhs)27 inline uint64_t BitOr(uint64_t rhs, uint64_t lhs) { return rhs | lhs; }
28
BitEx(uint64_t rhs,uint64_t lhs)29 inline uint64_t BitEx(uint64_t rhs, uint64_t lhs) { return rhs ^ lhs; }
30
BitAnd(uint64_t rhs,uint64_t lhs)31 inline uint64_t BitAnd(uint64_t rhs, uint64_t lhs) { return rhs & lhs; }
32
33 namespace mindspore {
34 namespace dataset {
Init()35 Status BuddySpace::Init() {
36 const uint64_t kBitOffset = 3;
37 const int kLvlMin = 3;
38 const int kLvlMax = 18;
39 if (log_min_ < 0) {
40 RETURN_STATUS_UNEXPECTED("log_min must be positive, but got: " + std::to_string(log_min_));
41 }
42 if (num_lvl_ < kLvlMin || num_lvl_ > kLvlMax) {
43 RETURN_STATUS_UNEXPECTED("num_lvl must be between 3 and 18, but got: " + std::to_string(num_lvl_));
44 }
45 min_ = BitLeftShift(1, log_min_);
46 max_ = BitLeftShift(1, log_min_ + num_lvl_ - 1);
47 size_t offset_1 = sizeof(rel_addr_t) * num_lvl_;
48 size_t offset_2 = sizeof(int) * num_lvl_ + offset_1;
49 size_t offset_3 = sizeof(char) * BitLeftShift(1, static_cast<uint64_t>(num_lvl_ - kBitOffset)) + offset_2;
50 try {
51 mem_ = std::make_unique<uint8_t[]>(offset_3);
52 } catch (const std::bad_alloc &e) {
53 RETURN_STATUS_OOM("Out of memory.");
54 }
55 CHECK_FAIL_RETURN_UNEXPECTED(memset_s(mem_.get(), offset_3, 0, offset_3) == EOK, "Failed to init memory to zero.");
56 auto ptr = mem_.get();
57 hint_ = reinterpret_cast<rel_addr_t *>(ptr);
58 count_ = reinterpret_cast<int *>((reinterpret_cast<char *>(ptr) + offset_1));
59 map_ = reinterpret_cast<char *>(ptr) + offset_2;
60 count_[num_lvl_ - 1] = 1;
61 map_[0] = BitOr(MORE_BIT, static_cast<uint64_t>(num_lvl_ - kBitOffset));
62 return Status::OK();
63 }
64
Alloc(const uint64_t sz,BSpaceDescriptor * desc,addr_t * p)65 Status BuddySpace::Alloc(const uint64_t sz, BSpaceDescriptor *desc, addr_t *p) noexcept {
66 RETURN_UNEXPECTED_IF_NULL(desc);
67 RETURN_UNEXPECTED_IF_NULL(p);
68 std::lock_guard<std::mutex> lock(mutex_);
69 addr_t addr = AllocNoLock(sz, desc);
70 if (addr != NOSPACE) {
71 *p = addr;
72 return Status::OK();
73 } else {
74 RETURN_STATUS_ERROR(StatusCode::kMDBuddySpaceFull, "BuddySpace full. Not an error. Please ignore.");
75 }
76 }
77
AllocNoLock(const uint64_t sz,BSpaceDescriptor * desc)78 addr_t BuddySpace::AllocNoLock(const uint64_t sz, BSpaceDescriptor *desc) noexcept {
79 MS_ASSERT(sz <= max_);
80 uint32_t reqSize = SizeToBlock(sz);
81 rel_addr_t rel_addr = AllocBuddySeg(reqSize);
82 if (rel_addr != static_cast<rel_addr_t>(NOSPACE)) {
83 if (memset_s(desc, sizeof(BSpaceDescriptor), 0, sizeof(BSpaceDescriptor)) != EOK) {
84 MS_LOG(ERROR) << "Failed to init memory to zero.";
85 return NOSPACE;
86 }
87 desc->sig = static_cast<int>(0xDEADBEEF);
88 desc->addr = rel_addr;
89 desc->req_size = reqSize;
90 desc->blk_size = NextPowerOf2(reqSize);
91 return static_cast<addr_t>(rel_addr * min_);
92 } else {
93 return NOSPACE;
94 }
95 }
96
FreeNoLock(const BSpaceDescriptor * desc)97 void BuddySpace::FreeNoLock(const BSpaceDescriptor *desc) {
98 MS_ASSERT(desc->sig == 0XDEADBEEF);
99 rel_addr_t rel_addr = desc->addr;
100 size_t blk_size = desc->blk_size;
101 size_t req_size = desc->req_size;
102 FreeBuddySeg(rel_addr, blk_size, req_size);
103 }
104
Free(const BSpaceDescriptor * desc)105 void BuddySpace::Free(const BSpaceDescriptor *desc) {
106 if (desc == nullptr) {
107 MS_LOG(ERROR) << "The pointer[desc] is null.";
108 return;
109 }
110 std::lock_guard<std::mutex> lock(mutex_);
111 return FreeNoLock(desc);
112 }
113
operator <<(std::ostream & os,const BuddySpace & s)114 std::ostream &operator<<(std::ostream &os, const BuddySpace &s) {
115 const int32_t kLvlOffset = 4;
116 os << "1 unit = " << s.GetMinSize() << "\n"
117 << "Size of buddy space = " << s.GetMaxSize() << "\n"
118 << "Number of levels = " << s.num_lvl_ << "\n\n"
119 << "Percent free = " << s.PercentFree() << "\n"
120 << "Dumping count array : "
121 << "\n";
122 for (int i = 0; i < s.num_lvl_; i++) {
123 os << "[" << i << "] = " << s.count_[i] << " ";
124 if (((i + 1) % kLvlOffset) == 0) {
125 os << "\n";
126 }
127 }
128 os << "\n";
129 os << "Dumping allocation info:"
130 << "\n";
131 auto max_addr = static_cast<rel_addr_t>(BitLeftShift(1, s.num_lvl_ - 1));
132 rel_addr_t addr = 0;
133 while (addr < max_addr) {
134 size_t sz = 0;
135 BuddySpace::STATE st;
136 s.GetBuddySegState(addr, &sz, &st);
137 os << "Address : " << std::left << std::setw(8) << addr << " Size : " << std::setw(8) << sz << " State : "
138 << ((st == BuddySpace::STATE::kAlloc) ? "ALLOC" : ((st == BuddySpace::STATE::kFree) ? "FREE" : "Unknown"))
139 << "\n";
140 addr += sz;
141 }
142 return os;
143 }
144
SizeToBlock(const uint64_t sz) const145 uint32_t BuddySpace::SizeToBlock(const uint64_t sz) const {
146 if (min_ == 0) {
147 MS_LOG(ERROR) << "min_ can not be zero.";
148 return 0;
149 }
150 uint32_t reqSize = (sz / min_);
151 if (sz % min_) {
152 reqSize++;
153 }
154 return reqSize;
155 }
156
GetBuddySegState(const rel_addr_t rel_addr,size_t * rel_sz,STATE * st) const157 void BuddySpace::GetBuddySegState(const rel_addr_t rel_addr, size_t *rel_sz, STATE *st) const {
158 const int32_t kAddrOffset = 4;
159 const int32_t kShiftOffset = 2;
160 const uint64_t kBitLeftShift = 2;
161 const uint64_t kPosOffset = 2;
162 char byte;
163 int pos;
164 int offset;
165 uint64_t val = 0;
166 int shift;
167 pos = BitRightShift(rel_addr, kPosOffset);
168 offset = rel_addr % kAddrOffset;
169 shift = offset * kShiftOffset;
170 byte = map_[pos];
171 switch (offset) {
172 case 0:
173 val = byte;
174 break;
175 case 1:
176 case 2:
177 val = BitLeftShift(BitAnd(byte, 0x0F), shift);
178 break;
179 case 3:
180 if (offset == 1) {
181 val = BitLeftShift(BitAnd(byte, 0x30), shift);
182 } else {
183 val = BitLeftShift(BitAnd(byte, 0x03), shift);
184 }
185 break;
186 }
187 if (BitAnd(val, ONE_BIT)) {
188 *rel_sz = 1;
189 } else if (BitAnd(val, TWO_BIT)) {
190 *rel_sz = 2;
191 } else if (BitAnd(val, MORE_BIT)) {
192 log_t lg = BitAnd(val, 0x0F);
193 *rel_sz = BitLeftShift(1, static_cast<uint64_t>(lg + kBitLeftShift));
194 } else {
195 *st = STATE::kEmpty;
196 return;
197 }
198 *st = BitAnd(val, ALLOC_BIT) ? STATE::kAlloc : STATE::kFree;
199 }
200
SetBuddySegState(rel_addr_t rel_addr,size_t rel_sz,STATE st)201 void BuddySpace::SetBuddySegState(rel_addr_t rel_addr, size_t rel_sz, STATE st) {
202 const int32_t kAddrOffset = 4;
203 const int32_t kShiftOffset = 2;
204 const uint64_t kBitOffset = 2;
205 const uint64_t kPosOffset = 2;
206 int clr;
207 int mask;
208 int pos;
209 int offset;
210 int val = 0;
211 int shift;
212 auto log_sz = static_cast<log_t>(Log2(rel_sz));
213 pos = BitRightShift(rel_addr, kPosOffset);
214 offset = rel_addr % kAddrOffset;
215 shift = offset * kShiftOffset;
216 if (rel_sz == 1) {
217 val = ONE_BIT;
218 mask = 0xC0;
219 } else if (rel_sz == 2) {
220 val = TWO_BIT;
221 mask = 0xF0;
222 } else {
223 val = BitOr(static_cast<uint64_t>(log_sz - kBitOffset), MORE_BIT);
224 mask = 0xFF;
225 }
226 if (st == STATE::kAlloc) {
227 val = BitOr(val, ALLOC_BIT);
228 } else if (st == STATE::kFree) {
229 val = BitAnd(val, ~(static_cast<uint64_t>(ALLOC_BIT)));
230 } else if (st == STATE::kEmpty) {
231 val = 0;
232 }
233 clr = static_cast<int>(~(BitRightShift(mask, shift)));
234 map_[pos] = static_cast<char>(BitAnd(map_[pos], clr));
235 map_[pos] = static_cast<char>(BitOr(map_[pos], BitRightShift(val, shift)));
236 if (st == STATE::kAlloc) {
237 count_[log_sz]--;
238 } else if (st == STATE::kFree) {
239 count_[log_sz]++;
240 if (rel_addr < hint_[log_sz]) {
241 hint_[log_sz] = rel_addr;
242 }
243 }
244 }
245
JoinBuddySeg(rel_addr_t addr,size_t blk_sz)246 void BuddySpace::JoinBuddySeg(rel_addr_t addr, size_t blk_sz) {
247 const int32_t kLogszStep = 2;
248 while (blk_sz < BitLeftShift(1, num_lvl_)) {
249 rel_addr_t buddy = BitEx(addr, blk_sz);
250 size_t sz = 0;
251 STATE st;
252 GetBuddySegState(buddy, &sz, &st);
253 if (st == STATE::kFree && sz == blk_sz) {
254 auto log_sz = static_cast<log_t>(Log2(blk_sz));
255 rel_addr_t left = (buddy < addr) ? buddy : addr;
256 rel_addr_t right = left + blk_sz;
257 MS_ASSERT(count_[log_sz] >= 2);
258 count_[log_sz] -= kLogszStep;
259 SetBuddySegState(right, blk_sz, STATE::kEmpty);
260 SetBuddySegState(left, BitLeftShift(blk_sz, 1), STATE::kFree);
261 for (int i = 0; i < log_sz; i++) {
262 if (hint_[i] == right) {
263 hint_[i] = left;
264 }
265 }
266 addr = left;
267 blk_sz <<= 1u;
268 } else {
269 break;
270 }
271 }
272 }
273
TrimBuddySeg(rel_addr_t addr,size_t blk_sz,size_t ask_sz)274 void BuddySpace::TrimBuddySeg(rel_addr_t addr, size_t blk_sz, size_t ask_sz) {
275 MS_ASSERT(ask_sz < blk_sz);
276 uint32_t inx = Log2(blk_sz);
277 size_t remaining_sz = ask_sz;
278 for (int i = inx; i > 0; i--) {
279 size_t b_size = BitLeftShift(1, i);
280 size_t half_sz = BitRightShift(b_size, 1);
281 count_[i]--;
282 SetBuddySegState(addr, half_sz, STATE::kFree);
283 SetBuddySegState(addr + half_sz, half_sz, STATE::kFree);
284 if (remaining_sz >= half_sz) {
285 SetBuddySegState(addr, half_sz, STATE::kAlloc);
286 remaining_sz -= half_sz;
287 if (remaining_sz == 0) {
288 break;
289 }
290 addr += half_sz;
291 }
292 }
293 }
294
UnTrimBuddySeg(rel_addr_t addr,size_t blk_sz,size_t ask_sz)295 void BuddySpace::UnTrimBuddySeg(rel_addr_t addr, size_t blk_sz, size_t ask_sz) {
296 MS_ASSERT(ask_sz < blk_sz);
297 uint32_t inx = Log2(blk_sz);
298 size_t remaining_sz = ask_sz;
299 for (int i = inx; i > 0; i--) {
300 size_t b_size = BitLeftShift(1, i);
301 size_t half_sz = BitRightShift(b_size, 1);
302 if (remaining_sz >= half_sz) {
303 #ifdef DEBUG
304 {
305 size_t sz = 0;
306 STATE st;
307 GetBuddySegState(addr, &sz, &st);
308 MS_ASSERT(sz == half_sz && st == STATE::kAlloc);
309 }
310 #endif
311 SetBuddySegState(addr, half_sz, STATE::kFree);
312 remaining_sz -= half_sz;
313 if (remaining_sz == 0) {
314 JoinBuddySeg(addr, half_sz);
315 break;
316 }
317 addr += half_sz;
318 }
319 }
320 }
321
AllocBuddySeg(uint32_t req_size)322 rel_addr_t BuddySpace::AllocBuddySeg(uint32_t req_size) noexcept {
323 uint32_t blk_size = NextPowerOf2(req_size);
324 int start_inx = static_cast<int>(Log2(blk_size));
325 bool found = false;
326 rel_addr_t ask_addr = 0;
327 auto max_addr = static_cast<rel_addr_t>(BitLeftShift(1, num_lvl_ - 1));
328 STATE st;
329 size_t sz = 0;
330 for (int i = start_inx; !found && i < num_lvl_; i++) {
331 MS_ASSERT(count_[i] >= 0);
332 if (count_[i] == 0) {
333 continue;
334 }
335 auto blk_sz = static_cast<size_t>(BitLeftShift(1, i));
336 ask_addr = hint_[i];
337 while (ask_addr < max_addr && !found) {
338 GetBuddySegState(ask_addr, &sz, &st);
339 if (st == STATE::kFree && sz == blk_sz) {
340 found = true;
341 } else {
342 MS_ASSERT(st != STATE::kEmpty);
343 ask_addr += ((sz > blk_sz) ? sz : blk_sz);
344 }
345 }
346 }
347 if (found) {
348 if (sz > req_size) {
349 TrimBuddySeg(ask_addr, sz, req_size);
350 } else {
351 SetBuddySegState(ask_addr, sz, STATE::kAlloc);
352 hint_[start_inx] = ask_addr;
353 }
354 return ask_addr;
355 } else {
356 return static_cast<rel_addr_t>(NOSPACE);
357 }
358 }
359
FreeBuddySeg(rel_addr_t addr,size_t blk_size,size_t req_size)360 void BuddySpace::FreeBuddySeg(rel_addr_t addr, size_t blk_size, size_t req_size) {
361 if (req_size == blk_size) {
362 #ifdef DEBUG
363 {
364 size_t sz = 0;
365 STATE st;
366 GetBuddySegState(addr, &sz, &st);
367 }
368 #endif
369 SetBuddySegState(addr, blk_size, STATE::kFree);
370 JoinBuddySeg(addr, blk_size);
371 } else {
372 UnTrimBuddySeg(addr, blk_size, req_size);
373 }
374 }
375
PercentFree() const376 int BuddySpace::PercentFree() const {
377 const int32_t kFloatToPercent = 100;
378 uint64_t total_free_sz = 0;
379 uint64_t max_sz_in_unit = BitLeftShift(1, num_lvl_ - 1);
380 // Go through the count array without lock
381 for (int i = 0; i < num_lvl_; i++) {
382 int cnt = count_[i];
383 if (cnt == 0) {
384 continue;
385 }
386 uint64_t blk_sz = BitLeftShift(1, i);
387 total_free_sz += (blk_sz * cnt);
388 }
389 return static_cast<int>(static_cast<float>(total_free_sz) / static_cast<float>(max_sz_in_unit) * kFloatToPercent);
390 }
391
BuddySpace(int log_min,int num_lvl)392 BuddySpace::BuddySpace(int log_min, int num_lvl)
393 : hint_(nullptr), count_(nullptr), map_(nullptr), log_min_(log_min), num_lvl_(num_lvl), min_(0), max_(0) {}
394
~BuddySpace()395 BuddySpace::~BuddySpace() {
396 hint_ = nullptr;
397 count_ = nullptr;
398 map_ = nullptr;
399 }
400
CreateBuddySpace(std::unique_ptr<BuddySpace> * out_bs,int log_min,int num_lvl)401 Status BuddySpace::CreateBuddySpace(std::unique_ptr<BuddySpace> *out_bs, int log_min, int num_lvl) {
402 Status rc;
403 auto bs = new (std::nothrow) BuddySpace(log_min, num_lvl);
404 if (bs == nullptr) {
405 RETURN_STATUS_OOM("Out of memory.");
406 }
407 rc = bs->Init();
408 if (rc.IsOk()) {
409 (*out_bs).reset(bs);
410 } else {
411 delete bs;
412 }
413 return rc;
414 }
415 } // namespace dataset
416 } // namespace mindspore
417