Allow the MemoryPool class to start with zero allocated memory and grow when more memory is needed

This commit is contained in:
Daniel Chappuis 2012-08-13 02:14:47 +02:00
parent fcac6457a7
commit 19f7925d47
5 changed files with 129 additions and 92 deletions

View File

@ -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<bodyindex, bodyindex> indexPair = addedPair->body1->getID() < addedPair->body2->getID() ? make_pair(addedPair->body1->getID(), addedPair->body2->getID()) :
make_pair(addedPair->body2->getID(), addedPair->body1->getID());

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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 T>
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<class T> const uint MemoryPool<T>::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<class T>
MemoryPool<T>::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; i<nbObjectsToAllocate; i++) {
// Get the adress of a memory unit
struct Unit* currentUnit = (struct Unit*)( (char*)pMemoryBlock + i * (sizeof(T) + sizeof(struct Unit)) );
currentUnit->pPrevious = NULL;
currentUnit->pNext = pFreeLinkedList;
if (pFreeLinkedList) {
pFreeLinkedList->pPrevious = currentUnit;
}
pFreeLinkedList = currentUnit;
}
MemoryPool<T>::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<class T>
MemoryPool<T>::~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<class T>
void* MemoryPool<T>::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<class T>
void MemoryPool<T>::freeObject(void* pObjectToFree) {
// The pointer location must be inside the memory block
assert(pMemoryBlock<pObjectToFree && pObjectToFree<(void*)((char*)pMemoryBlock + memorySize));
//assert(pBlocks<pObjectToFree && pObjectToFree<(void*)((char*)pBlocks + memorySize));
struct Unit* currentUnit = (struct Unit*)((char*)pObjectToFree - sizeof(struct Unit));
pAllocatedLinkedList = currentUnit->pNext;
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<class T>
void MemoryPool<T>::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; i<nbObjectsNextBlock; i++) {
// Get the adress of a memory unit
struct MemoryUnit* currentUnit = (struct MemoryUnit*)( (char*)pBlocks + i * (sizeof(T) + sizeof(struct MemoryUnit)) );
currentUnit->pPrevious = 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<class T>
uint MemoryPool<T>::getMaxNbObjects() const {
return maxNbObjects;
uint MemoryPool<T>::getCapacity() const {
return capacity;
}
// Return the current number of objects in the pool