From 19f7925d47177d5a9d715a23626becaa8cfb73bb Mon Sep 17 00:00:00 2001 From: Daniel Chappuis Date: Mon, 13 Aug 2012 02:14:47 +0200 Subject: [PATCH] Allow the MemoryPool class to start with zero allocated memory and grow when more memory is needed --- src/collision/CollisionDetection.cpp | 14 +- src/collision/broadphase/PairManager.h | 2 +- src/configuration.h | 1 - src/engine/PhysicsWorld.cpp | 2 +- src/memory/MemoryPool.h | 202 +++++++++++++++---------- 5 files changed, 129 insertions(+), 92 deletions(-) diff --git a/src/collision/CollisionDetection.cpp b/src/collision/CollisionDetection.cpp index 3a3dd322..b60e3fbc 100644 --- a/src/collision/CollisionDetection.cpp +++ b/src/collision/CollisionDetection.cpp @@ -46,8 +46,7 @@ using namespace std; // Constructor CollisionDetection::CollisionDetection(PhysicsWorld* world) - : world(world), memoryPoolContacts(NB_MAX_CONTACTS), memoryPoolOverlappingPairs(NB_MAX_COLLISION_PAIRS), - narrowPhaseGJKAlgorithm(memoryPoolContactInfos), memoryPoolContactInfos(NB_MAX_CONTACTS), + : world(world), narrowPhaseGJKAlgorithm(memoryPoolContactInfos), narrowPhaseSphereVsSphereAlgorithm(memoryPoolContactInfos) { // Create the broad-phase algorithm that will be used (Sweep and Prune with AABB) @@ -86,7 +85,7 @@ bool CollisionDetection::computeCollisionDetection() { double startTime = timeValueStart.tv_sec * 1000000.0 + (timeValueStart.tv_usec); double stopTime = timeValueStop.tv_sec * 1000000.0 + (timeValueStop.tv_usec); double deltaTime = stopTime - startTime; - printf("Broadphase time : %f micro sec \n", deltaTime); + //printf("Broadphase time : %f micro sec \n", deltaTime); // Compute the narrow-phase collision detection bool collisionExists = computeNarrowPhase(); @@ -108,10 +107,10 @@ void CollisionDetection::computeBroadPhase() { broadPhaseAlgorithm->updateObject(*it, *((*it)->getAABB())); } } - + // TODO : DELETE THIS - std::cout << "Nb overlapping pairs : " << overlappingPairs.size() << std::endl; - std::cout << "Nb active pairs in pair manager : " << broadPhaseAlgorithm->getNbOverlappingPairs() << std::endl; + //std::cout << "Nb overlapping pairs : " << overlappingPairs.size() << std::endl; + //std::cout << "Nb active pairs in pair manager : " << broadPhaseAlgorithm->getNbOverlappingPairs() << std::endl; } // Compute the narrow-phase collision detection @@ -165,8 +164,7 @@ bool CollisionDetection::computeNarrowPhase() { // Allow the broadphase to notify the collision detection about an overlapping pair // This method is called by a broad-phase collision detection algorithm void CollisionDetection::broadPhaseNotifyAddedOverlappingPair(const BroadPhasePair* addedPair) { - std::cout << "New overlapping pair : id0=" << addedPair->body1->getID() << ", id=" << addedPair->body2->getID() << std::endl; - + // Construct the pair of body index pair indexPair = addedPair->body1->getID() < addedPair->body2->getID() ? make_pair(addedPair->body1->getID(), addedPair->body2->getID()) : make_pair(addedPair->body2->getID(), addedPair->body1->getID()); diff --git a/src/collision/broadphase/PairManager.h b/src/collision/broadphase/PairManager.h index 0121fda9..23c8813c 100644 --- a/src/collision/broadphase/PairManager.h +++ b/src/collision/broadphase/PairManager.h @@ -76,7 +76,7 @@ class PairManager { luint computeNextPowerOfTwo(luint number) const; // Return the next power of two void reallocatePairs(); // Reallocate memory for more pairs void shrinkMemory(); // Shrink the allocated memory - bodyindex computePairOffset(const BroadPhasePair* pair) const; // Compute the offset of a given pair + bodyindex computePairOffset(const BroadPhasePair* pair) const; // Compute the offset of a given pair BroadPhasePair* lookForAPair(bodyindex id1, bodyindex id2, luint hashValue) const; // Look for a pair in the set of overlapping pairs BroadPhasePair* findPairWithHashValue(bodyindex id1, bodyindex id2, diff --git a/src/configuration.h b/src/configuration.h index b1a12f81..ac9eefb8 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -70,7 +70,6 @@ const reactphysics3d::decimal PERSISTENT_CONTACT_DIST_THRESHOLD = 0.02; // Dist const int NB_MAX_BODIES = 100000; // Maximum number of bodies const int NB_MAX_CONTACTS = 100000; // Maximum number of contacts (for memory pool allocation) const int NB_MAX_CONSTRAINTS = 100000; // Maximum number of constraints -const int NB_MAX_COLLISION_PAIRS = 10000; // Maximum number of collision pairs of bodies (for memory pool allocation) // Constraint solver constants const uint DEFAULT_LCP_ITERATIONS = 15; // Number of iterations when solving a LCP problem diff --git a/src/engine/PhysicsWorld.cpp b/src/engine/PhysicsWorld.cpp index 9f6e1ad3..88571601 100644 --- a/src/engine/PhysicsWorld.cpp +++ b/src/engine/PhysicsWorld.cpp @@ -34,7 +34,7 @@ using namespace std; // Constructor PhysicsWorld::PhysicsWorld(const Vector3& gravity) - : gravity(gravity), isGravityOn(true), currentBodyID(0), memoryPoolRigidBodies(NB_MAX_BODIES) { + : gravity(gravity), isGravityOn(true), currentBodyID(0) { } // Destructor diff --git a/src/memory/MemoryPool.h b/src/memory/MemoryPool.h index 29e41172..49fbc182 100644 --- a/src/memory/MemoryPool.h +++ b/src/memory/MemoryPool.h @@ -36,12 +36,6 @@ // TODO : Check that casting is done correctly in this class using // C++ cast operator like reinterpret_cast<>, ... -// TODO : Rename the variable "maxNbObjects" into "capacity" - -// TODO : Replace uint by luint in this class - -// TODO : Make the memory pool to start without allocated memory and to grow -// by using several blocks instead of only one. // ReactPhysics3D namespace namespace reactphysics3d { @@ -58,135 +52,181 @@ template class MemoryPool { private: - // Unit of memory - struct Unit { - struct Unit* pNext; // Pointer to the next memory unit - struct Unit* pPrevious; // Pointer to the previous memory unit + // MemoryUnit represents a unit of memory + struct MemoryUnit { + struct MemoryUnit* pNext; // Pointer to the next memory unit + struct MemoryUnit* pPrevious; // Pointer to the previous memory unit }; - void* pMemoryBlock; // Pointer to the whole memory block - struct Unit* pAllocatedLinkedList; // Pointer to the linked list of allocated memory units - struct Unit* pFreeLinkedList; // Pointer to the linked list of free memory units - size_t memorySize; // Total allocated memory in the pool - uint currentNbObjects; // Current number of objects in the pool - const uint maxNbObjects; // Maximum number of objects in the pool + // Memory block (that contains several memory units) + struct MemoryBlock { + struct MemoryBlock* pNext; // Pointer to the next memory block + }; + + static const uint NB_OBJECTS_FIRST_BLOCK; // Number of objects allocated in the first block + void* pBlocks; // Pointer to the first allocated memory block + struct MemoryUnit* pAllocatedUnits; // Pointer to the first allocated memory unit + struct MemoryUnit* pFreeUnits; // Pointer to the first free memory unit + uint currentNbObjects; // Current number of objects in the pool + uint capacity; // Current maximum number of objects that can be allocated in the pool + uint nbObjectsNextBlock; // Number of objects to allocate in the next block + void allocateMemory(); // Allocate more memory (more blocks) when needed public: - MemoryPool(uint nbObjectsToAllocate) throw(std::bad_alloc); // Constructor - ~MemoryPool(); // Destructor + MemoryPool(uint capacity = 0) throw(std::bad_alloc); // Constructor + ~MemoryPool(); // Destructor - uint getMaxNbObjects() const; // Return the maximum number of objects allowed in the pool + uint getCapacity() const; // Return the current maximum number of objects allowed in the pool uint getCurrentNbObjects() const; // Return the current number of objects in the pool void* allocateObject(); // Return a pointer to an memory allocated location to store a new object void freeObject(void* pObjectToFree); // Tell the pool that an object doesn't need to be store in the pool anymore }; +// static member definition +template const uint MemoryPool::NB_OBJECTS_FIRST_BLOCK = 100; // Constructor // Allocate a large block of memory in order to contain // a given number of object of the template type T template -MemoryPool::MemoryPool(uint nbObjectsToAllocate) throw(std::bad_alloc) - : currentNbObjects(0), maxNbObjects(nbObjectsToAllocate) { - pMemoryBlock = NULL; - pAllocatedLinkedList = NULL; - pFreeLinkedList = NULL; - - // Compute the total memory size that need to be allocated - memorySize = nbObjectsToAllocate * (sizeof(struct Unit) + sizeof(T)); - - // Allocate the whole memory block - pMemoryBlock = malloc(memorySize); - - // Check that the allocation didn't fail - if (!pMemoryBlock) throw std::bad_alloc(); - - // For each allocated memory unit - for (uint i=0; ipPrevious = NULL; - currentUnit->pNext = pFreeLinkedList; - - if (pFreeLinkedList) { - pFreeLinkedList->pPrevious = currentUnit; - } - - pFreeLinkedList = currentUnit; - } +MemoryPool::MemoryPool(uint capacity) throw(std::bad_alloc) + : currentNbObjects(0), capacity(capacity) { + pBlocks = 0; + pAllocatedUnits = 0; + pFreeUnits = 0; + nbObjectsNextBlock = (capacity == 0) ? NB_OBJECTS_FIRST_BLOCK : capacity; + // Allocate the first memory block if the capacity is + // different from zero + if (capacity) allocateMemory(); } // Destructor -// Deallocate the block of memory that has been allocated previously +// Deallocate the blocks of memory that have been allocated previously template MemoryPool::~MemoryPool() { // Check if we have a memory leak assert(currentNbObjects == 0); - // Release the whole memory block - free(pMemoryBlock); + // Release all the allocated memory blocks + struct MemoryBlock* currentBlock = (struct MemoryBlock*) pBlocks; + while(currentBlock != 0) { + MemoryBlock* tempBlock = currentBlock->pNext; + free(currentBlock); + currentBlock = tempBlock; + } } -// Return a pointer to an memory allocated location to store a new object -// This method does not allocate memory because it has already been done at the -// beginning but it returns a pointer to a location in the allocated block of -// memory where a new object can be stored +// Return a pointer to a memory allocated location to store a new object +// This method only allocates memory if needed and it returns a pointer +// to a location in an allocated block of memory where a new object can be stored template void* MemoryPool::allocateObject() { - // If no memory unit is available in the current allocated memory block - assert(currentNbObjects < maxNbObjects); - assert(pFreeLinkedList); - - struct Unit* currentUnit = pFreeLinkedList; - pFreeLinkedList = currentUnit->pNext; - if (pFreeLinkedList) { - pFreeLinkedList->pPrevious = NULL; + + // If there is not enough allocated memory in the pool + if (currentNbObjects == capacity) { + + // Allocate a new memory block + allocateMemory(); } - currentUnit->pNext = pAllocatedLinkedList; - if (pAllocatedLinkedList) { - pAllocatedLinkedList->pPrevious = currentUnit; + assert(currentNbObjects < capacity); + assert(pFreeUnits); + + struct MemoryUnit* currentUnit = pFreeUnits; + pFreeUnits = currentUnit->pNext; + if (pFreeUnits) { + pFreeUnits->pPrevious = 0; } - pAllocatedLinkedList = currentUnit; + + currentUnit->pNext = pAllocatedUnits; + if (pAllocatedUnits) { + pAllocatedUnits->pPrevious = currentUnit; + } + pAllocatedUnits = currentUnit; currentNbObjects++; // Return a pointer to the allocated memory unit - return (void*)((char*)currentUnit + sizeof(struct Unit)); + return (void*)((char*)currentUnit + sizeof(struct MemoryUnit)); } -// Tell the pool that an object doesn't need to be store in the pool anymore +// Tell the pool that an object does not need to be stored in the pool anymore // This method does not deallocate memory because it will be done only at the -// end but it informs the memory pool that an object that was stored in the heap +// end but it notifies the memory pool that an object that was stored in the pool // does not need to be stored anymore and therefore we can use the corresponding // location in the pool for another object template void MemoryPool::freeObject(void* pObjectToFree) { + // The pointer location must be inside the memory block - assert(pMemoryBlockpNext; - if (pAllocatedLinkedList) { - pAllocatedLinkedList->pPrevious = NULL; + struct MemoryUnit* currentUnit = (struct MemoryUnit*)((char*)pObjectToFree - sizeof(struct MemoryUnit)); + pAllocatedUnits = currentUnit->pNext; + if (pAllocatedUnits) { + pAllocatedUnits->pPrevious = 0; } - currentUnit->pNext = pFreeLinkedList; - if (pFreeLinkedList) { - pFreeLinkedList->pPrevious = currentUnit; + currentUnit->pNext = pFreeUnits; + if (pFreeUnits) { + pFreeUnits->pPrevious = currentUnit; } - pFreeLinkedList = currentUnit; + pFreeUnits = currentUnit; currentNbObjects--; } +// Allocate more memory. This method is called when there are no +// free memory units available anymore. Therefore, we need to allocate +// a new memory block in order to be able to store several new memory units. +template +void MemoryPool::allocateMemory() { + + // Compute the size of the new + size_t sizeBlock = nbObjectsNextBlock * (sizeof(struct MemoryUnit) + sizeof(T)); + + struct MemoryBlock* tempBlocks = (struct MemoryBlock*) pBlocks; + + // Allocate a new memory block + pBlocks = malloc(sizeBlock); + + // Check that the allocation didn't fail + if (!pBlocks) throw std::bad_alloc(); + + struct MemoryBlock* block = (struct MemoryBlock*) pBlocks; + block->pNext = tempBlocks; + + // For each allocated memory unit in the new block + for (uint i=0; ipPrevious = 0; + currentUnit->pNext = pFreeUnits; + + if (pFreeUnits) { + pFreeUnits->pPrevious = currentUnit; + } + + pFreeUnits = currentUnit; + } + + // Update the current capacity of the memory pool + capacity += nbObjectsNextBlock; + + // The next block will be two times the size of the last + // allocated memory block + nbObjectsNextBlock *= 2; +} + + // Return the maximum number of objects allowed in the pool template -uint MemoryPool::getMaxNbObjects() const { - return maxNbObjects; +uint MemoryPool::getCapacity() const { + return capacity; } // Return the current number of objects in the pool