• 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(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