• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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