AGX Dynamics 2.42.1.1
Loading...
Searching...
No Matches
MemoryPool.h
Go to the documentation of this file.
1/*
2Copyright 2007-2025. Algoryx Simulation AB.
3
4All AGX source code, intellectual property, documentation, sample code,
5tutorials, scene files and technical white papers, are copyrighted, proprietary
6and confidential material of Algoryx Simulation AB. You may not download, read,
7store, distribute, publish, copy or otherwise disseminate, use or expose this
8material unless having a written signed agreement with Algoryx Simulation AB, or having been
9advised so by Algoryx Simulation AB for a time limited evaluation, or having purchased a
10valid commercial license from Algoryx Simulation AB.
11
12Algoryx Simulation AB disclaims all responsibilities for loss or damage caused
13from using this software, unless otherwise stated in written agreements with
14Algoryx Simulation AB.
15*/
16
17#ifndef AGX_MEMORYPOOL_H
18#define AGX_MEMORYPOOL_H
19
20#include <agx/config.h>
21#include <cstring>
22#include <agx/Allocator.h>
23#include <agx/Vector.h>
24#include <limits>
25
26namespace agx { template <typename T> class MemoryPool; }
27
28
29// template <typename T>
30// inline void *operator new(size_t size, agx::MemoryPool<T>& pool)
31// {
32// agxAssert(size == sizeof(T));
33// return pool.allocate();
34// }
35
36
37namespace agx
38{
39 #define AGX_CHUNK_ALIGNMENT 128
40 #define AGX_CHUNK_GROW_FACTOR 1.5
41
46 {
47 public:
48 BatchAllocator(const char *name = "BatchAllocator", uint32_t initialChunkSize = 64);
52
53 void *allocate(uint32_t numBytes);
54 void *allocate(uint32_t numBytes, uint32_t alignment);
55 void deallocate(void *buffer);
56 void clear();
57
58 size_t size();
59
60 protected:
61 void createChunk(uint32_t size);
63
64 struct Chunk
65 {
66 char *buffer;
67 char *end;
68 char *head;
70 };
71
73 uint32_t m_chunkSize;
74 };
75
76
78 {
79 public:
80 virtual void deallocateVirtual(void *ptr) = 0;
82 };
83
87 template <typename T>
89 {
90 public:
94 MemoryPool(const char *name = "MemoryPool", uint32_t initialChunkSize = 64);
95
99 virtual ~MemoryPool();
100
101
106
110 void deallocate(T *element);
111
115 T *create();
116
120 void destroy(T *element);
121
122
126 void clear();
127
131 void clearFast();
132
133 // void *allocateArray(uint32_t numElements);
134 // void deallocateArray(T *buffer);
135
136
137 virtual void deallocateVirtual(void *ptr);
138
139 size_t size() const { return m_size; }
140
141 private:
142 size_t m_size;
143 T *m_freeHead;
144 };
145
146
147
148 //---------------------------------------------------------------
149
150 template <typename T>
152 {
153 public:
154 MemoryPoolOld(size_t initialSize = 0, size_t chunkSize = 64);
156
160 void clear();
161
165 void setChunkSize( size_t size );
166
168 size_t getChunkSize() const;
169
173 T *getElement();
174
179
184 T *getElement(const T& initializer);
185
186
188 size_t size() const;
189
191 size_t capacity() const;
192
194 size_t memUsed() const;
195
199 void flush();
200
205 void garbageCollect();
206
207
212 void setAutoGarbageCollect( int n );
213
215 int getAutoGarbageCollect( ) const;
216
220 void setEnableCallConstructor( bool flag );
221
225 void setMaxAllocationSize( size_t numBytes );
226
227 private:
228 void createChunk();
229 void deleteChunk();
230
231 private:
232 typedef Vector<T *> ChunkVector;
233 ChunkVector m_chunks;
234 size_t m_numUsed;
235 size_t m_chunkSize;
236 size_t m_numFlushes;
237 size_t m_lastAllocatedNumElements;
238 float m_averageNumElements;
239 int m_flushCount;
240 bool m_callConstructor;
241 size_t m_maxAllocationSize;
242 size_t m_capacity;
243 size_t m_chunkIndex;
244 size_t m_elementIndex;
245
246 typedef agx::Vector<size_t> SizeVector;
247 SizeVector m_sizeVector;
248 };
249
250 /* Implementation */
251
252 template <typename T>
253 MemoryPoolOld<T>::MemoryPoolOld(size_t initialSize, size_t chunkSize) :
254 m_numUsed(0), m_chunkSize(chunkSize), m_numFlushes(0), m_lastAllocatedNumElements(0), m_averageNumElements(0),
255 m_flushCount( std::numeric_limits<int>::max() ), m_callConstructor(true),
256 m_maxAllocationSize( std::numeric_limits<size_t>::max() ), m_capacity( 0 ), m_chunkIndex(0), m_elementIndex(0)
257 {
258 size_t numChunks = initialSize ? (initialSize / chunkSize) + 1 : 0;
259 for (size_t i = 0; i < numChunks; i++)
260 this->createChunk();
261 }
262
263 template <typename T>
265 {
266 clear();
267 }
268
269 template <typename T>
271 {
272 m_chunkSize = size;
273 }
274
275 template <typename T>
277 {
278 return m_chunkSize;
279 }
280
281 template <typename T>
283 {
284 return sizeof(T)*capacity();
285 }
286
287
288 template <typename T>
290 {
291 return m_numUsed;
292 }
293
294 template <typename T>
296 {
297 m_callConstructor = flag;
298 }
299
300 template <typename T>
301 inline void MemoryPoolOld<T>::setMaxAllocationSize(size_t numBytes )
302 {
303 m_maxAllocationSize = numBytes;
304 }
305
306
307 template <typename T>
309 {
310 return m_capacity;
311 }
312
313 template <typename T>
315 {
316 m_flushCount = n;
317 }
318
319 template <typename T>
321 {
322 return m_flushCount;
323 }
324
325 template <typename T>
327 {
328 m_numUsed++;
329
330 if ( m_numUsed > this->capacity() )
331 this->createChunk();
332 else if ( m_elementIndex >= m_sizeVector[ m_chunkIndex] )
333 {
334 m_elementIndex =0;
335 m_chunkIndex++;
336 }
337
338
339 T* element = m_chunks[m_chunkIndex]+m_elementIndex;
340 m_elementIndex++;
341
342 if (m_callConstructor)
343 ::new((void *)element) T();
344
345 return element;
346 }
347
348
349 template <typename T>
350 inline T *MemoryPoolOld<T>::getElement(const T& initializer)
351 {
352 // size_t numElementsInChunk = m_sizeVector[m_chunkIndex];
353
354 m_numUsed++;
355 m_elementIndex++;
356
357 if (m_numUsed > this->capacity() || m_elementIndex >= m_sizeVector[ m_chunkIndex] )
358 this->createChunk();
359
360
361 T* element = m_chunks[m_chunkIndex]+m_elementIndex;
362
363 if (m_callConstructor)
364 ::new((void *)element) T(initializer);
365
366 return element;
367 }
368
369
370 template <typename T>
372 {
373 // Free ALL memory used
374 while (!m_chunks.empty())
375 this->deleteChunk();
376
377 m_capacity = 0;
378 m_chunkIndex=0;
379 m_numUsed = 0;
380 m_elementIndex=0;
381 m_lastAllocatedNumElements=0;
382 m_sizeVector.clear(Container::SHRINK_BUFFER);
383 }
384
385 template <typename T>
387 {
388 // allocate..
389 T* ptr = static_cast<T*>( allocateBytes( sizeof(T) * m_chunkSize ) );
390
391 // .. and call default constructors
392 for (size_t i=0; i < m_chunkSize; ++i)
393 ::new( (void*) &ptr[i]) T();
394
395 // Create one chunk, increase capacity
396 m_chunks.push_back(ptr);
397 m_sizeVector.push_back( m_chunkSize );
398 m_capacity += m_chunkSize;
399 m_chunkIndex = m_chunks.size()-1;
400 m_elementIndex=0;
401 }
402
403 template <typename T>
404 void MemoryPoolOld<T>::deleteChunk()
405 {
406 T* ptr = m_chunks.back();
407
408 for (size_t i=0; i < m_sizeVector.back(); ++i)
409 ptr[i].~T();
410
411 deallocateBytes( ptr );
412
413 // reduce capacity
414 m_capacity -= m_sizeVector.back();
415
416 m_sizeVector.pop_back();
417
418 m_chunks.pop_back();
419 m_chunkIndex = m_chunks.size();
420
421 if (m_chunks.size())
422 m_chunkIndex = m_chunks.size()-1;
423
424 m_elementIndex=0;
425 }
426
427 template <typename T>
429 {
430 // Store the average number of elements used
431 m_lastAllocatedNumElements = m_numUsed;
432 m_averageNumElements = m_averageNumElements*0.3f + 0.7f*m_lastAllocatedNumElements;
433
434 m_numFlushes++;
435 m_numUsed=0;
436 m_chunkIndex=0;
437 m_elementIndex=0;
438
439 // Should we do a garbageCollect now?
440 if (m_flushCount > 0 && (int)m_numFlushes > m_flushCount)
441 garbageCollect();
442 }
443
444 template <typename T>
446
447 // If no flushes are done, we cannot determine the average size
448 if (!m_numFlushes || !m_chunks.size())
449 return;
450
451 // Remove any chunks exceeding the average used size
452 size_t sum=0;
453 size_t endIdx=m_sizeVector.size()-1;
454
455 for(size_t i=0; i < m_sizeVector.size(); i++)
456 {
457 sum += m_sizeVector[i];
458 if (sum >= m_averageNumElements) {
459 endIdx = i;
460 break;
461 }
462 }
463
464 size_t n=m_chunks.size();
465 for(size_t i=n-1; i > endIdx; i--)
466 deleteChunk();
467
468 m_sizeVector.resize( m_chunks.size() );
469
470 m_capacity = sum;
471
472 m_chunkIndex=0;
473 m_numUsed = 0;
474 m_elementIndex=0;
475 }
476
477
478
479 //---------------------------------------------------------------
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496 /* Implementation */
497 inline void *BatchAllocator::allocate(uint32_t numBytes)
498 {
499 if (m_firstChunk->head + numBytes > m_firstChunk->end)
500 this->createChunk( std::max(m_chunkSize, numBytes) );
501
502 void* ret = m_firstChunk->head;
503 m_firstChunk->head += numBytes;
504
505 return ret;
506 }
507
508 inline void *BatchAllocator::allocate(uint32_t numBytes, uint32_t alignment)
509 {
510 agxAssert(isPowerOfTwo(alignment));
511 // m_firstChunk->head += (alignment-1) & (alignment - ((size_t)m_firstChunk->head & (alignment-1)));
512 m_firstChunk->head += (alignment-1);
513 m_firstChunk->head = (char *) ((size_t)m_firstChunk->head & ~((size_t)alignment-1));
514 return this->allocate(numBytes);
515 }
516
517 inline void BatchAllocator::deallocate(void *buffer)
518 {
519 agxVerify(buffer); // Just for the unused variable warning.
520 /* We can only reuse memory if deallocating the last allocated block */
521 /*
522 agxAssert(buffer);
523
524 if (buffer == m_prev && m_prev >= m_firstChunk->buffer && m_prev < m_firstChunk->end)
525 {
526 m_head = m_prev;
527 m_prev = 0;
528 }
529 */
530 }
531
532
533
534 inline size_t BatchAllocator::size()
535 {
536 size_t totSize = 0;
537
538 Chunk *chunk = m_firstChunk;
539 while (chunk)
540 {
541 totSize += chunk->end - chunk->buffer;
542 chunk = chunk->next;
543 }
544
545 return totSize;
546 }
547
548
549
550
551
552 template <typename T>
553 MemoryPool<T>::MemoryPool(const char *name, uint32_t initialChunkSize)
554 : BatchAllocator(name, (uint32_t)(initialChunkSize * sizeof(T))), m_size(0), m_freeHead(0)
555 {
556 #ifdef AGX_DEBUG
557 size_t warningHack = 0;
558 #endif
559 agxAssert( sizeof(void*) <= (sizeof(T)+warningHack) );
560 }
561
562 template <typename T>
564 {
565 this->clear();
566 }
567
568
569
570 template <typename T>
572 {
573 m_size++;
574 if ( m_freeHead != 0 )
575 {
576 T* ret = m_freeHead;
577 m_freeHead = (T*) ( *((void**)( m_freeHead )) );
578 return ret;
579 }
580
581 return static_cast<T *>( BatchAllocator::allocate(sizeof(T)) );
582 }
583
584 template <typename T>
586 {
587 void *element = this->allocate();
588 return ::new(element) T();
589 }
590
591
592 template <typename T>
593 inline void MemoryPool<T>::deallocate(T *element)
594 {
595 m_size--;
596 // store previous head
597 *((void**)element) = m_freeHead;
598 m_freeHead = element;
599 }
600
601
602 template <typename T>
603 inline void MemoryPool<T>::destroy(T *element)
604 {
605 element->~T();
606 this->deallocate(element);
607 }
608
609 template <typename T>
611 {
612 this->deallocate(static_cast<T *>(ptr));
613 }
614
615 template <typename T>
617 {
618 m_size = 0;
619 // If we have free elements, there is a linked list within the chunks to the free slots
620 if ( this->m_freeHead )
621 {
622 agx::Vector<size_t> chunkSizes;
623
624 char *bitmap;
625 chunkSizes.push_back( 0 );
626
627 for( BatchAllocator::Chunk *c = this->m_firstChunk; c != 0; c= c->next )
628 {
629 chunkSizes.push_back( chunkSizes.back() + ((c->end - c->buffer) / sizeof(T)) );
630 }
631
632 // keep a bitmap, to avoid / and % calculations, use 1 byte/element
633 bitmap = (char *)this->allocateBytes( chunkSizes.back() );
634 memset( bitmap, 0, chunkSizes.back() );
635
636 // traverse linked list..
637 for ( void* l = this->m_freeHead; l != 0; l = *((void**)l) )
638 {
639 // find chunk,
640 int idx = 0;
641 BatchAllocator::Chunk *tmp = this->m_firstChunk;
642 while ( tmp && !( tmp->buffer <= l && l <= tmp->head ) )
643 {
644 ++idx;
645 tmp = tmp->next;
646 }
647
648 agxAssert(tmp);
649 // tag in bitmap
650 if (tmp) {
651 bitmap[ chunkSizes[idx] + ((char*)l - tmp->buffer) / sizeof(T) ] = 1;
652 }
653 }
654
655 // for every chunk,
656 // check bitmap and call d-tors.
657 int idx = 0;
658 for ( BatchAllocator::Chunk* tmp = this->m_firstChunk; tmp!=0; tmp=tmp->next, ++idx )
659 {
660 size_t end = (tmp->head - tmp->buffer) / sizeof(T);
661 T* buff = reinterpret_cast<T*>(tmp->buffer);
662
663 for (size_t e = 0; e < end; ++e)
664 if ( !bitmap[ chunkSizes[idx] + e] )
665 buff[e].~T();
666 }
667
668
669 this->deallocateBytes( bitmap );
670 }
671 else
672 {
673 //
674 // All allocated elements are in use:
675 // for every chunk, for every element, run destructor
676
677 BatchAllocator::Chunk *c = this->m_firstChunk;
678
679 while ( c )
680 {
681 T* element = reinterpret_cast<T *>(c->buffer);
682 while ( (char *)element < c->head )
683 {
684 element->~T();
685 ++element;
686 }
687 c = c->next;
688 }
689 }
690
691 this->m_freeHead = 0;
693 }
694
695
696 template <typename T>
698 {
699 m_size = 0;
702 agxAssert1(!this->m_freeHead, "TODO");
703 this->m_freeHead = 0;
705 }
706
707
708}
709
710
711#endif /* _AGX_MEMORYPOOL_H_ */
712
#define AGXCORE_EXPORT
#define m_size
Definition: agx/Vector.h:429
#define m_capacity
Definition: agx/Vector.h:430
virtual void deallocateVirtual(void *ptr)=0
virtual ~AbstractMemoryPool()
Definition: MemoryPool.h:81
Batch allocator.
Definition: MemoryPool.h:46
Chunk * m_firstChunk
Definition: MemoryPool.h:72
void deallocate(void *buffer)
Definition: MemoryPool.h:517
size_t deleteAllChunks()
void * allocate(uint32_t numBytes)
Definition: MemoryPool.h:497
void createChunk(uint32_t size)
BatchAllocator(const char *name="BatchAllocator", uint32_t initialChunkSize=64)
BatchAllocator & operator=(const BatchAllocator &other)
BatchAllocator(const BatchAllocator &other)
uint32_t m_chunkSize
Definition: MemoryPool.h:73
Byte allocator.
Definition: Allocator.h:55
@ SHRINK_BUFFER
Buffer is deallocated and replaced by an newly allocated empty buffer.
Definition: Container.h:44
void setAutoGarbageCollect(int n)
Set the number of calls to flush which will trigger a call to garbageCollect.
Definition: MemoryPool.h:314
size_t memUsed() const
Definition: MemoryPool.h:282
void setChunkSize(size_t size)
Set the new size of the chunk.
Definition: MemoryPool.h:270
void garbageCollect()
Free memory not in use.
Definition: MemoryPool.h:445
size_t size() const
Definition: MemoryPool.h:289
size_t getChunkSize() const
Definition: MemoryPool.h:276
MemoryPoolOld(size_t initialSize=0, size_t chunkSize=64)
Definition: MemoryPool.h:253
size_t capacity() const
\ return the total number of elements allocated
Definition: MemoryPool.h:308
void flush()
Return all elements to container.
Definition: MemoryPool.h:428
void clear()
Clear all memory allocated by this MemoryPoolOld.
Definition: MemoryPool.h:371
void setEnableCallConstructor(bool flag)
Set to true if the constructor of the elements should be called BEFORE they are returned in getElemen...
Definition: MemoryPool.h:295
int getAutoGarbageCollect() const
Definition: MemoryPool.h:320
void setMaxAllocationSize(size_t numBytes)
Set the largest number of bytes a call to new will be.
Definition: MemoryPool.h:301
Memory pool.
Definition: MemoryPool.h:89
virtual void deallocateVirtual(void *ptr)
Definition: MemoryPool.h:610
void destroy(T *element)
Deallocates an element and calls its destructor.
Definition: MemoryPool.h:603
void deallocate(T *element)
Deallocates one element, no destructor called.
Definition: MemoryPool.h:593
size_t size() const
Definition: MemoryPool.h:139
T * allocate()
Allocates one element, no constructor called.
Definition: MemoryPool.h:571
T * create()
Allocates an element and calls its default constructor.
Definition: MemoryPool.h:585
void clear()
Clears the memory pool and runs the destructor on every allocated object.
Definition: MemoryPool.h:616
void clearFast()
Clears the memory pool without calling destructors.
Definition: MemoryPool.h:697
virtual ~MemoryPool()
Destructor.
Definition: MemoryPool.h:563
MemoryPool(const char *name="MemoryPool", uint32_t initialChunkSize=64)
Constructor.
Definition: MemoryPool.h:553
Templated vector class.
Definition: agx/Vector.h:53
T & back() const
Definition: agx/Vector.h:707
void push_back(const T2 &value)
Definition: agx/Vector.h:715
#define agxAssert1(expr, msg)
Definition: debug.h:144
#define agxVerify(expr)
Definition: debug.h:131
#define agxAssert(expr)
Definition: debug.h:143
#define AGX_FORCE_INLINE
Definition: macros.h:58
The agx namespace contains the dynamics/math part of the AGX Dynamics API.
bool isPowerOfTwo(T value)
Definition: Math.h:408
LinearProbingHashSetImplementation< KeyT, HashT >::iterator end(LinearProbingHashSetImplementation< KeyT, HashT > &set)
Vec3T< T > max(const Vec3T< T > &lhs, const Vec3T< T > &rhs)
Definition: Vec3Template.h:855
STL namespace.