1 /* 2 * Copyright (c) 2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 #ifndef UTIL_SLAB_HPP 17 #define UTIL_SLAB_HPP 18 19 #include <new> 20 #include <vector> 21 #include <mutex> 22 #ifdef FFRT_BBOX_ENABLE 23 #include <unordered_set> 24 #endif 25 #ifndef _MSC_VER 26 #include <sys/mman.h> 27 #endif 28 #include "sync/sync.h" 29 30 namespace ffrt { 31 const std::size_t BatchAllocSize = 128 * 1024; 32 33 template <typename T, size_t MmapSz = BatchAllocSize> 34 class SimpleAllocator { 35 public: 36 SimpleAllocator(SimpleAllocator const&) = delete; 37 void operator=(SimpleAllocator const&) = delete; 38 instance()39 static SimpleAllocator<T>* instance() 40 { 41 static SimpleAllocator<T> ins; 42 return &ins; 43 } 44 45 // NOTE: call constructor after allocMem allocMem()46 static T* allocMem() 47 { 48 return instance()->alloc(); 49 } 50 51 // NOTE: call destructor before freeMem freeMem(T * t)52 static void freeMem(T* t) 53 { 54 t->~T(); 55 instance()->free(t); 56 } 57 58 // only used for BBOX getUnfreedMem()59 static std::vector<T*> getUnfreedMem() 60 { 61 return instance()->getUnfreed(); 62 } 63 private: 64 #ifdef MUTEX_PERF // Mutex Lock&Unlock Cycles Statistic 65 xx::mutex lock {"SimpleAllocator::lock"}; 66 #else 67 fast_mutex lock; 68 #endif 69 std::vector<T*> primaryCache; 70 #ifdef FFRT_BBOX_ENABLE 71 std::unordered_set<T*> secondaryCache; 72 #endif 73 T* basePtr = nullptr; 74 uint32_t count = 0; 75 getUnfreed()76 std::vector<T*> getUnfreed() 77 { 78 lock.lock(); 79 std::vector<T*> ret; 80 #ifdef FFRT_BBOX_ENABLE 81 ret.reserve(MmapSz / sizeof(T) + secondaryCache.size()); 82 for (std::size_t i = 0; i < MmapSz / sizeof(T); ++i) { 83 if (basePtr != nullptr && 84 std::find(primaryCache.begin(), primaryCache.end(), &basePtr[i]) == primaryCache.end()) { 85 ret.push_back(&basePtr[i]); 86 } 87 } 88 for (auto ite = secondaryCache.cbegin(); ite != secondaryCache.cend(); ite++) { 89 ret.push_back(*ite); 90 } 91 #endif 92 lock.unlock(); 93 return ret; 94 } 95 init()96 void init() 97 { 98 basePtr = reinterpret_cast<T*>(::operator new(MmapSz)); 99 count = MmapSz / sizeof(T); 100 primaryCache.reserve(count); 101 for (std::size_t i = 0; i < count; ++i) { 102 primaryCache.push_back(&basePtr[i]); 103 } 104 } 105 alloc()106 T* alloc() 107 { 108 lock.lock(); 109 T* t = nullptr; 110 if (count == 0) { 111 if (basePtr != nullptr) { 112 t = reinterpret_cast<T*>(::operator new(sizeof(T))); 113 #ifdef FFRT_BBOX_ENABLE 114 secondaryCache.insert(t); 115 #endif 116 lock.unlock(); 117 return t; 118 } 119 init(); 120 } 121 t = primaryCache.back(); 122 primaryCache.pop_back(); 123 count--; 124 lock.unlock(); 125 return t; 126 } 127 free(T * t)128 void free(T* t) 129 { 130 lock.lock(); 131 if (basePtr != nullptr && 132 basePtr <= t && 133 static_cast<size_t>(reinterpret_cast<uintptr_t>(t)) < 134 static_cast<size_t>(reinterpret_cast<uintptr_t>(basePtr)) + MmapSz) { 135 primaryCache.push_back(t); 136 count++; 137 } else { 138 ::operator delete(t); 139 #ifdef FFRT_BBOX_ENABLE 140 secondaryCache.erase(t); 141 #endif 142 } 143 lock.unlock(); 144 } 145 SimpleAllocator()146 SimpleAllocator() 147 { 148 } ~SimpleAllocator()149 ~SimpleAllocator() 150 { 151 #ifdef FFRT_BBOX_ENABLE 152 for (auto ite = secondaryCache.cbegin(); ite != secondaryCache.cend(); ite++) { 153 ::operator delete(*ite); 154 } 155 #endif 156 if (basePtr) { 157 ::operator delete(basePtr); 158 } 159 } 160 }; 161 162 #ifndef _MSC_VER 163 template <typename T, std::size_t MmapSz = 16 * 1024 * 1024> 164 class QSimpleAllocator { instance(std::size_t size)165 static QSimpleAllocator<T, MmapSz>* instance(std::size_t size) 166 { 167 static QSimpleAllocator<T, MmapSz> ins(size); 168 return &ins; 169 } 170 std::size_t TSize; 171 std::mutex lock; 172 std::vector<T*> cache; 173 uint32_t flags = MAP_ANONYMOUS | MAP_PRIVATE; 174 expand()175 bool expand() 176 { 177 const int prot = PROT_READ | PROT_WRITE; 178 std::size_t sz = (TSize + 15UL) & -16UL; 179 char* p = reinterpret_cast<char*>(mmap(nullptr, MmapSz, prot, flags, -1, 0)); 180 if (p == (char*)MAP_FAILED) { 181 if ((flags & MAP_HUGETLB) != 0) { 182 flags = MAP_ANONYMOUS | MAP_PRIVATE; 183 p = reinterpret_cast<char*>(mmap(nullptr, MmapSz, prot, flags, -1, 0)); 184 } 185 if (p == (char*)MAP_FAILED) { 186 perror("mmap"); 187 return false; 188 } 189 } 190 for (std::size_t i = 0; i + sz <= MmapSz; i += sz) { 191 cache.push_back(reinterpret_cast<T*>(p + i)); 192 } 193 return true; 194 } 195 alloc()196 T* alloc() 197 { 198 T* p = nullptr; 199 lock.lock(); 200 if (cache.empty()) { 201 if (!expand()) { 202 lock.unlock(); 203 return nullptr; 204 } 205 } 206 p = cache.back(); 207 cache.pop_back(); 208 lock.unlock(); 209 return p; 210 } 211 free(T * p)212 void free(T* p) 213 { 214 lock.lock(); 215 cache.push_back(p); 216 lock.unlock(); 217 } 218 QSimpleAllocator()219 QSimpleAllocator() 220 { 221 } 222 223 public: 224 explicit QSimpleAllocator(std::size_t size = sizeof(T)) 225 { 226 TSize = size; 227 } 228 QSimpleAllocator(QSimpleAllocator const&) = delete; 229 void operator=(QSimpleAllocator const&) = delete; 230 231 static T* allocMem(std::size_t size = sizeof(T)) 232 { 233 return instance(size)->alloc(); 234 } 235 236 static void freeMem(T* p, std::size_t size = sizeof(T)) 237 { 238 instance(size)->free(p); 239 } 240 }; 241 #endif 242 } // namespace ffrt 243 #endif /* UTIL_SLAB_H */ 244