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