fmatvec  0.0.0
_memory.h
1/* Copyright (C) 2003-2005 Martin Förg
2
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2.1 of the License, or (at your option) any later version.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 *
17 * Contact:
18 * martin.o.foerg@googlemail.com
19 *
20 */
21
22#ifndef _memory_h
23#define _memory_h
24
25// includes
26#include <cstdlib>
27#include <vector>
28#include <memory>
29
30// include header given by ALLOCATORHEADER
31#ifdef ALLOCATORHEADER
32# define CPPBEGININCLUDE <
33# define CPPENDINCLUDE >
34# include CPPBEGININCLUDE./ALLOCATORHEADER CPPENDINCLUDE
35# undef CPPBEGININCLUDE
36# undef CPPENDINCLUDE
37#endif
38// use fmatvec::PoolAllocator if no allocator is defined (the default in the configure script)
39#ifndef ALLOCATORCLASS
40# define ALLOCATORCLASS fmatvec::PoolAllocator
41#endif
42
43// create some locking and atomic operations dependent on the thread safety method
44//
45// FMATVEC_LOCKVAR(l) : create a locking/mutex named l
46// FMATVEC_LOCKINIT(l) : initialize the locking/mutex named l
47// FMATVEC_LOCK(l) : lock the locking/mutex l
48// FMATVEC_UNLOCK(l) : unlock the locking/mutex l
49//
50// FMATVEC_SYNCVAR(l) : create a locking/mutex named l for a syncronized operation
51// FMATVEC_SYNCINIT(l) : initialize a locking/mutex named l for a syncronized operation
52// FMATVEC_SYNCINC(var, l) : var++ (syncronized by l)
53// FMATVEC_SYNCPREDEC(ret, var, l) : ret=--(var) (syncronized by l)
54#if defined FMATVEC_THREADSAFE_GCCBUILTIN
55 #define FMATVEC_LOCKVAR(l) short int l;
56 #define FMATVEC_LOCKINIT(l) l=0;
57 #define FMATVEC_LOCK(l) while(__sync_bool_compare_and_swap(&l, 0, 0xffff)==false);
58 #define FMATVEC_UNLOCK(l) __sync_bool_compare_and_swap(&l, 0xffff, 0);
59
60 #define FMATVEC_SYNCVAR(l)
61 #define FMATVEC_SYNCINIT(l)
62 #define FMATVEC_SYNCINC(var, l) __sync_add_and_fetch(&(var), 1);
63 #define FMATVEC_SYNCPREDEC(ret, var, l) ret=__sync_sub_and_fetch(&(var), 1);
64#elif defined FMATVEC_THREADSAFE_PTHREAD
65 #include <pthread.h> // pthread required a header file
66
67 #define FMATVEC_LOCKVAR(l) pthread_mutex_t l;
68 #define FMATVEC_LOCKINIT(l) pthread_mutex_init(&l, NULL);
69 #define FMATVEC_LOCK(l) pthread_mutex_lock(&l);
70 #define FMATVEC_UNLOCK(l) pthread_mutex_unlock(&l);
71
72 #define FMATVEC_SYNCVAR(l) pthread_mutex_t l;
73 #define FMATVEC_SYNCINIT(l) pthread_mutex_init(&l, NULL);
74 #define FMATVEC_SYNCINC(var, l) pthread_mutex_lock(&l); (var)++; pthread_mutex_unlock(&l);
75 #define FMATVEC_SYNCPREDEC(ret, var, l) pthread_mutex_lock(&l); ret=--(var); pthread_mutex_unlock(&l);
76#elif defined FMATVEC_THREADSAFE_OPENMP
77 #define FMATVEC_PRAGMA(x) _Pragma(#x) // openmp required the _Pragma operator (defined by C99)
78
79 #define FMATVEC_LOCKVAR(l)
80 #define FMATVEC_LOCKINIT(l)
81 #define FMATVEC_LOCK(l) FMATVEC_PRAGMA(omp critical (l)) {
82 #define FMATVEC_UNLOCK(l) }
83
84 #define FMATVEC_SYNCVAR(l)
85 #define FMATVEC_SYNCINIT(l)
86 #define FMATVEC_SYNCINC(var, l) FMATVEC_PRAGMA(omp critical (l)) { (var)++; }
87 #define FMATVEC_SYNCPREDEC(ret, var, l) FMATVEC_PRAGMA(omp critical (l)) { ret=--(var); }
88#else
89 #define FMATVEC_PRAGMA(l)
90 #define FMATVEC_LOCKVAR(l)
91 #define FMATVEC_LOCKINIT(l)
92 #define FMATVEC_LOCK(l)
93 #define FMATVEC_UNLOCK(l)
94
95 #define FMATVEC_SYNCVAR(l)
96 #define FMATVEC_SYNCINIT(l)
97 #define FMATVEC_SYNCINC(var, l) (var)++;
98 #define FMATVEC_SYNCPREDEC(ret, var, l) ret=--(var);
99#endif
100
107namespace fmatvec {
108
110
111 // A STL-like pool memory allocator (being fast)
112 template <class AT>
113 class PoolAllocator {
114 private:
115 // The minimal allocation size in power of 2: MINSIZE=2^MINEXP
116 static const size_t MINEXP=4;
117 // A pool of memory of allocation size 2^(MINEXP+0), 2^(MINEXP+1), 2^(MINEXP+2), ...
118 // stored at index 0, 1, 2, ... in memoryPool.
119 // The pointers stored here are already allocated but currently not used.
120 std::vector<std::vector<AT*>> memoryPool;
121 // Just a dummy variable used for zero size allocations.
122 AT sizeZero;
123 FMATVEC_LOCKVAR(fmatvec_memoryPoolLock)
124 public:
125 // Constructor: alloc memoryPool with at least size 1, since the size is doubled each time the size ran out.
126 PoolAllocator() : memoryPool(1) {
127 FMATVEC_LOCKINIT(fmatvec_memoryPoolLock)
128 }
129 // Destructor: delete all cached memory in the pool.
130 ~PoolAllocator() {
131 // loop over all pool sizes and free memory
132 for(typename std::vector<std::vector<AT*>>::iterator i=memoryPool.begin(); i!=memoryPool.end(); ++i)
133 for(typename std::vector<AT*>::iterator j=i->begin(); j!=i->end(); ++j)
134 delete[](*j);
135 }
136 // Allocate new memory of at least size size.
137 AT *allocate(size_t size) {
138 // do nothing for zero size, but return a valid memory pointer
139 if(size==0) return &sizeZero;
140 // calculate size index: size 0 to 2^MINEXP = index 0; size 2^MINEXP+1 to 2*2^MINEXP = index 1; ...
141 size_t index=0;
142 --size>>=MINEXP-1;
143 while(size>>=1) ++index;
144 AT* ret=NULL;
145 FMATVEC_LOCK(fmatvec_memoryPoolLock)
146 // increase (double) memoryPool if nessasary
147 if(index>=memoryPool.size()) memoryPool.resize(index<<1);
148 // try to use cached memory
149 std::vector<AT*> &memoryPoolIndex=memoryPool[index];
150 if(memoryPoolIndex.size()>0) {
151 ret=memoryPoolIndex.back(); // get last cached memory
152 memoryPoolIndex.pop_back(); // remove last cached memory
153 }
154 FMATVEC_UNLOCK(fmatvec_memoryPoolLock)
155 if(ret) return ret; // return chaced memory
156 // return new memory
157 return new AT[1<<(index+MINEXP)]; // allocate 2^(index+MINEXP) elements (max size for this index)
158 }
159 // Free memory and release it to the pool.
160 void deallocate(AT *p, size_t size) {
161 // do nothing for zero size, a dummy but valid pointer was returned by allocate
162 if(size==0) return;
163 // calculate size index: size 0 to 2^MINEXP = index 0; size 2^MINEXP+1 to 2*2^MINEXP = index 1; ...
164 size_t index=0;
165 --size>>=MINEXP-1;
166 while(size>>=1) ++index;
167 FMATVEC_LOCK(fmatvec_memoryPoolLock)
168 // release memory
169 memoryPool[index].push_back(p); // add to cached memory
170 FMATVEC_UNLOCK(fmatvec_memoryPoolLock)
171 }
172 };
173
174#ifdef MEMORYCLASS_FMATVEC
175
176 // this is the old fmatvec Memory class which uses different allocators (using the std::allocator interface)
177 template <class AT> class Memory {
178 private:
179 typedef ALLOCATORCLASS<AT> allocator_type;
180 size_t sz;
181 AT *ele0;
182 static allocator_type ms;
183 size_t *ref;
184 FMATVEC_SYNCVAR(fmatvec_refLock)
185 void lock() {
186 FMATVEC_SYNCINC(*ref, fmatvec_refLock)
187 }
188 void unlock() {
189 size_t refLocal;
190 FMATVEC_SYNCPREDEC(refLocal, *ref, fmatvec_refLock)
191 if(!refLocal) {
192 ms.deallocate(ele0, sz);
193 delete ref;
194 }
195 }
196
197 public:
198 Memory() : sz(0), ele0(0), ref(new size_t(1)) {
199 FMATVEC_SYNCINIT(fmatvec_refLock);
200 }
201
202 Memory(size_t n) : sz(n), ele0(ms.allocate(sz)), ref(new size_t(1)){
203 FMATVEC_SYNCINIT(fmatvec_refLock);
204 }
205
206 Memory(const Memory &memory) : sz(memory.sz), ele0(memory.ele0), ref(memory.ref) {
207 FMATVEC_SYNCINIT(fmatvec_refLock);
208 lock();
209 }
210
211 ~Memory() {
212 unlock();
213 }
214
215 Memory& operator=(const Memory &memory) {
216 if(this == &memory)
217 return *this;
218 unlock(); sz=memory.sz; ref=memory.ref; ele0=memory.ele0; lock();
219 return *this;
220 }
221
222 void resize(size_t n) {
223 unlock();
224 ref=new size_t(1);
225 sz = n;
226 ele0 = ms.allocate(sz);
227 }
228
229 AT* get() const {return ele0;}
230 };
231
232#elif defined MEMORYCLASS_STDSHAREDPTR
233
234 // Just a wrapper class around std::shared_ptr<T[]>, since the Memory interface differs a little bit from the std smart-ptr interface
235 // This wrapper is fully inlined by the compiler if optimization is used!
236 template <class AT> class Memory {
237 private:
239
240 public:
241 Memory() : ele0() {}
242
243 Memory(size_t n) : ele0(new AT[n]) {}
244
245 Memory(const Memory &memory) : ele0(memory.ele0) {}
246
247 ~Memory() {}
248
249 Memory& operator=(const Memory &memory) {
250 if(this == &memory)
251 return *this;
252 ele0=memory.ele0;
253 return *this;
254 }
255
256 void resize(size_t n) {
257 ele0.reset(new AT[n]);
258 }
259
260 AT* get() const {return ele0.get();}
261 };
262
263#else
264
265 // This class uses an "intrusive reference counting" mechanism for memory of n elements of type AT.
266 // The reference count is stored as the first element of allocate memory. The rest is used for the n element of type AT.
267 // Hence at least sizeof(size_t)+n*sizeof(AT) bytes are allocated.
268 // For correct memory alignment the memory is allocated as an array of AT.
269 template <class AT> class Memory {
270 private:
271 static const size_t ele0Start=(sizeof(size_t)-1)/sizeof(AT)+1; // the minimum number of AT elements such that one size_t fits into
272 size_t *ref; // a pointer to the reference count (this is also the address of the allocated memory)
273 AT *ele0; // a pointer to the allocated memory (this is also the address ((AT*)ref)+1)
274 FMATVEC_SYNCVAR(fmatvec_refLock)
275
276 void lock() {
277 FMATVEC_SYNCINC(*ref, fmatvec_refLock)
278 }
279
280 void unlock() {
281 #if __GNUC__ >= 12
282 // gcc >= 12 triggers a false positive on this code
283 #pragma GCC diagnostic push
284 #pragma GCC diagnostic ignored "-Wuse-after-free"
285 #endif
286 size_t refLocal;
287 FMATVEC_SYNCPREDEC(refLocal, *ref, fmatvec_refLock)
288 if(!refLocal) {
289 delete[]reinterpret_cast<AT*>(ref);
290 }
291 #if __GNUC__ >= 12
292 #pragma GCC diagnostic pop
293 #endif
294 }
295
296 public:
297 Memory() : ref(reinterpret_cast<size_t*>(new AT[ele0Start])), ele0(nullptr) {
298 *ref=1; // initialize the reference count to 1
299 FMATVEC_SYNCINIT(fmatvec_refLock);
300 }
301
302 Memory(size_t n) : ref(reinterpret_cast<size_t*>(new AT[ele0Start+n])), ele0(reinterpret_cast<AT*>(ref)+ele0Start) {
303 *ref=1; // initialize the reference count to 1
304 FMATVEC_SYNCINIT(fmatvec_refLock);
305 }
306
307 Memory(const Memory &memory) : ref(memory.ref), ele0(memory.ele0) {
308 FMATVEC_SYNCINIT(fmatvec_refLock);
309 lock();
310 }
311
312 ~Memory() {
313 unlock();
314 }
315
316 Memory& operator=(const Memory &memory) {
317 if(this == &memory)
318 return *this;
319 unlock(); ref=memory.ref; ele0=memory.ele0; lock();
320 return *this;
321 }
322
323 void resize(size_t n) {
324 unlock();
325 ref=reinterpret_cast<size_t*>(new AT[ele0Start+n]);
326 ele0=reinterpret_cast<AT*>(ref)+ele0Start;
327 *ref=1; // initialize the reference count to 1
328 }
329
330 AT* get() const {return ele0;}
331 };
332
333#endif
334
335}
336
338
339#endif
Definition: types.h:85
Namespace fmatvec.
Definition: _memory.cc:28