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