Add new memory allocator HeapAllocator
This commit is contained in:
parent
581f642280
commit
77940a43f7
|
@ -169,6 +169,7 @@ SET (REACTPHYSICS3D_HEADERS
|
|||
"src/memory/MemoryAllocator.h"
|
||||
"src/memory/PoolAllocator.h"
|
||||
"src/memory/SingleFrameAllocator.h"
|
||||
"src/memory/HeapAllocator.h"
|
||||
"src/memory/DefaultAllocator.h"
|
||||
"src/memory/MemoryManager.h"
|
||||
"src/containers/Stack.h"
|
||||
|
@ -267,6 +268,7 @@ SET (REACTPHYSICS3D_SOURCES
|
|||
"src/mathematics/Vector3.cpp"
|
||||
"src/memory/PoolAllocator.cpp"
|
||||
"src/memory/SingleFrameAllocator.cpp"
|
||||
"src/memory/HeapAllocator.cpp"
|
||||
"src/memory/MemoryManager.cpp"
|
||||
"src/utils/Profiler.cpp"
|
||||
"src/utils/Logger.cpp"
|
||||
|
|
|
@ -180,6 +180,9 @@ void DynamicsWorld::solveContactsAndConstraints(decimal timeStep) {
|
|||
}
|
||||
|
||||
mContactSolverSystem.storeImpulses();
|
||||
|
||||
// Reset the contact solver
|
||||
mContactSolverSystem.reset();
|
||||
}
|
||||
|
||||
// Solve the position error correction of the constraints
|
||||
|
|
272
src/memory/HeapAllocator.cpp
Normal file
272
src/memory/HeapAllocator.cpp
Normal file
|
@ -0,0 +1,272 @@
|
|||
/********************************************************************************
|
||||
* ReactPhysics3D physics library, http://www.reactphysics3d.com *
|
||||
* Copyright (c) 2010-2018 Daniel Chappuis *
|
||||
*********************************************************************************
|
||||
* *
|
||||
* This software is provided 'as-is', without any express or implied warranty. *
|
||||
* In no event will the authors be held liable for any damages arising from the *
|
||||
* use of this software. *
|
||||
* *
|
||||
* Permission is granted to anyone to use this software for any purpose, *
|
||||
* including commercial applications, and to alter it and redistribute it *
|
||||
* freely, subject to the following restrictions: *
|
||||
* *
|
||||
* 1. The origin of this software must not be misrepresented; you must not claim *
|
||||
* that you wrote the original software. If you use this software in a *
|
||||
* product, an acknowledgment in the product documentation would be *
|
||||
* appreciated but is not required. *
|
||||
* *
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be *
|
||||
* misrepresented as being the original software. *
|
||||
* *
|
||||
* 3. This notice may not be removed or altered from any source distribution. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
// Libraries
|
||||
#include "HeapAllocator.h"
|
||||
#include "MemoryManager.h"
|
||||
#include <cstdlib>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
|
||||
using namespace reactphysics3d;
|
||||
|
||||
size_t HeapAllocator::INIT_ALLOCATED_SIZE = 1024;
|
||||
|
||||
// Constructor
|
||||
HeapAllocator::HeapAllocator(MemoryAllocator& baseAllocator)
|
||||
: mBaseAllocator(baseAllocator), mAllocatedMemory(0), mMemoryUnits(nullptr), mCachedFreeUnit(nullptr),
|
||||
mNbTimesAllocateMethodCalled(0), mDebug(baseAllocator) {
|
||||
|
||||
reserve(INIT_ALLOCATED_SIZE);
|
||||
}
|
||||
|
||||
// Destructor
|
||||
HeapAllocator::~HeapAllocator() {
|
||||
|
||||
for (auto it = mDebug.begin(); it != mDebug.end(); ++it) {
|
||||
std::cout << "Size: " << (*it).first << " -> " << (*it).second << std::endl;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Check that the allocate() and release() methods have been called the same
|
||||
// number of times to avoid memory leaks.
|
||||
assert(mNbTimesAllocateMethodCalled == 0);
|
||||
#endif
|
||||
|
||||
// Release the memory allocated for memory unit
|
||||
MemoryUnitHeader* unit = mMemoryUnits;
|
||||
while (unit != nullptr) {
|
||||
|
||||
assert(!unit->isAllocated);
|
||||
|
||||
MemoryUnitHeader* nextUnit = unit->nextUnit;
|
||||
|
||||
// Destroy the unit
|
||||
unit->~MemoryUnitHeader();
|
||||
mBaseAllocator.release(static_cast<void*>(unit), unit->size + sizeof(MemoryUnitHeader));
|
||||
|
||||
unit = nextUnit;
|
||||
}
|
||||
}
|
||||
|
||||
/// Split a memory unit in two units. One of size "size" and the second with
|
||||
/// left over space. The second unit is put into the free memory units
|
||||
void HeapAllocator::splitMemoryUnit(MemoryUnitHeader* unit, size_t size) {
|
||||
|
||||
assert(size <= unit->size);
|
||||
assert(!unit->isAllocated);
|
||||
|
||||
// Split the free memory unit in two memory units, one with the requested memory size
|
||||
// and a second one with the left over space
|
||||
if (size + sizeof(MemoryUnitHeader) < unit->size) {
|
||||
|
||||
assert(unit->size - size > 0);
|
||||
|
||||
// Create a new memory unit with left over space
|
||||
unsigned char* newUnitLocation = (reinterpret_cast<unsigned char*>(unit)) + sizeof(MemoryUnitHeader) + size;
|
||||
MemoryUnitHeader* newUnit = new (static_cast<void*>(newUnitLocation)) MemoryUnitHeader(unit->size - sizeof(MemoryUnitHeader) - size, unit, unit->nextUnit, unit->isNextContiguousMemory);
|
||||
assert(newUnit->nextUnit != newUnit);
|
||||
unit->nextUnit = newUnit;
|
||||
if (newUnit->nextUnit != nullptr) {
|
||||
newUnit->nextUnit->previousUnit = newUnit;
|
||||
}
|
||||
assert(unit->nextUnit != unit);
|
||||
unit->isNextContiguousMemory = true;
|
||||
unit->size = size;
|
||||
|
||||
assert(unit->previousUnit == nullptr || unit->previousUnit->nextUnit == unit);
|
||||
assert(unit->nextUnit == nullptr || unit->nextUnit->previousUnit == unit);
|
||||
|
||||
assert(newUnit->previousUnit == nullptr || newUnit->previousUnit->nextUnit == newUnit);
|
||||
assert(newUnit->nextUnit == nullptr || newUnit->nextUnit->previousUnit == newUnit);
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate memory of a given size (in bytes) and return a pointer to the
|
||||
// allocated memory.
|
||||
void* HeapAllocator::allocate(size_t size) {
|
||||
|
||||
assert(size > 0);
|
||||
|
||||
// We cannot allocate zero bytes
|
||||
if (size == 0) return nullptr;
|
||||
|
||||
if (mDebug.containsKey(size)) {
|
||||
mDebug[size]++;
|
||||
}
|
||||
else {
|
||||
mDebug.add(Pair<size_t, int>(size, 1));
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
mNbTimesAllocateMethodCalled++;
|
||||
#endif
|
||||
|
||||
MemoryUnitHeader* currentUnit = mMemoryUnits;
|
||||
assert(mMemoryUnits->previousUnit == nullptr);
|
||||
|
||||
// If there is a cached free memory unit
|
||||
if (mCachedFreeUnit != nullptr) {
|
||||
assert(!mCachedFreeUnit->isAllocated);
|
||||
|
||||
// If the cached free memory unit matches the request
|
||||
if (size <= mCachedFreeUnit->size) {
|
||||
currentUnit = mCachedFreeUnit;
|
||||
mCachedFreeUnit = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// For each memory unit
|
||||
while (currentUnit != nullptr) {
|
||||
|
||||
// If we have found a free memory unit with size large enough for the allocation request
|
||||
if (!currentUnit->isAllocated && size <= currentUnit->size) {
|
||||
|
||||
// Split the free memory unit in two memory units, one with the requested memory size
|
||||
// and a second one with the left over space
|
||||
splitMemoryUnit(currentUnit, size);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
currentUnit = currentUnit->nextUnit;
|
||||
}
|
||||
|
||||
// If we have not found a large enough memory unit we need to allocate more memory
|
||||
if (currentUnit == nullptr) {
|
||||
|
||||
reserve((mAllocatedMemory + size) * 2);
|
||||
|
||||
assert(mCachedFreeUnit != nullptr);
|
||||
assert(!mCachedFreeUnit->isAllocated);
|
||||
|
||||
// The cached free memory unit is large enough at this point
|
||||
currentUnit = mCachedFreeUnit;
|
||||
|
||||
assert(currentUnit->size >= size);
|
||||
|
||||
splitMemoryUnit(currentUnit, size);
|
||||
}
|
||||
|
||||
currentUnit->isAllocated = true;
|
||||
|
||||
// Cache the next memory unit if it is not allocated
|
||||
if (currentUnit->nextUnit != nullptr && !currentUnit->nextUnit->isAllocated) {
|
||||
mCachedFreeUnit = currentUnit->nextUnit;
|
||||
}
|
||||
|
||||
// Return a pointer to the memory area inside the unit
|
||||
return static_cast<void*>(reinterpret_cast<unsigned char*>(currentUnit) + sizeof(MemoryUnitHeader));
|
||||
}
|
||||
|
||||
// Release previously allocated memory.
|
||||
void HeapAllocator::release(void* pointer, size_t size) {
|
||||
|
||||
assert(size > 0);
|
||||
|
||||
// Cannot release a 0-byte allocated memory
|
||||
if (size == 0) return;
|
||||
|
||||
mDebug[size]--;
|
||||
if (mDebug[size] == 0) {
|
||||
mDebug.remove(size);
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
mNbTimesAllocateMethodCalled--;
|
||||
#endif
|
||||
|
||||
unsigned char* unitLocation = static_cast<unsigned char*>(pointer) - sizeof(MemoryUnitHeader);
|
||||
MemoryUnitHeader* unit = reinterpret_cast<MemoryUnitHeader*>(unitLocation);
|
||||
assert(unit->isAllocated);
|
||||
unit->isAllocated = false;
|
||||
|
||||
MemoryUnitHeader* currentUnit = unit;
|
||||
|
||||
// If the previous unit is not allocated and memory is contiguous to the current unit
|
||||
if (unit->previousUnit != nullptr && !unit->previousUnit->isAllocated && unit->previousUnit->isNextContiguousMemory) {
|
||||
|
||||
currentUnit = unit->previousUnit;
|
||||
|
||||
// Merge the two contiguous memory units
|
||||
mergeUnits(unit->previousUnit, unit);
|
||||
}
|
||||
|
||||
// If the next unit is not allocated and memory is contiguous to the current unit
|
||||
if (currentUnit->nextUnit != nullptr && !currentUnit->nextUnit->isAllocated && currentUnit->isNextContiguousMemory) {
|
||||
|
||||
// Merge the two contiguous memory units
|
||||
mergeUnits(currentUnit, currentUnit->nextUnit);
|
||||
}
|
||||
|
||||
mCachedFreeUnit = currentUnit;
|
||||
}
|
||||
|
||||
// Merge two contiguous memory units that are not allocated.
|
||||
/// Memory unit 2 will be merged into memory unit 1 and memory unit 2 will be removed
|
||||
void HeapAllocator::mergeUnits(MemoryUnitHeader* unit1, MemoryUnitHeader* unit2) {
|
||||
|
||||
assert(unit2->previousUnit == unit1);
|
||||
assert(unit1->nextUnit == unit2);
|
||||
assert(!unit1->isAllocated);
|
||||
assert(!unit2->isAllocated);
|
||||
assert(unit1->isNextContiguousMemory);
|
||||
|
||||
unit1->size += unit2->size + sizeof(MemoryUnitHeader);
|
||||
unit1->nextUnit = unit2->nextUnit;
|
||||
assert(unit1->nextUnit != unit1);
|
||||
if (unit2->nextUnit != nullptr) {
|
||||
unit2->nextUnit->previousUnit = unit1;
|
||||
}
|
||||
unit1->isNextContiguousMemory = unit2->isNextContiguousMemory;
|
||||
|
||||
// Destroy unit 2
|
||||
unit2->~MemoryUnitHeader();
|
||||
|
||||
assert(unit1->previousUnit == nullptr || unit1->previousUnit->nextUnit == unit1);
|
||||
assert(unit1->nextUnit == nullptr || unit1->nextUnit->previousUnit == unit1);
|
||||
}
|
||||
|
||||
// Reserve more memory for the allocator
|
||||
void HeapAllocator::reserve(size_t sizeToAllocate) {
|
||||
|
||||
// Allocate memory
|
||||
void* memory = mBaseAllocator.allocate(sizeToAllocate + sizeof(MemoryUnitHeader));
|
||||
assert(memory != nullptr);
|
||||
|
||||
// Create a new memory unit for the allocated memory
|
||||
MemoryUnitHeader* memoryUnit = new (memory) MemoryUnitHeader(sizeToAllocate, nullptr, mMemoryUnits, false);
|
||||
|
||||
if (mMemoryUnits != nullptr) {
|
||||
mMemoryUnits->previousUnit = memoryUnit;
|
||||
}
|
||||
|
||||
// Add the memory unit at the beginning of the linked-list of memory units
|
||||
mMemoryUnits = memoryUnit;
|
||||
|
||||
mCachedFreeUnit = mMemoryUnits;
|
||||
|
||||
mAllocatedMemory += sizeToAllocate;
|
||||
}
|
149
src/memory/HeapAllocator.h
Normal file
149
src/memory/HeapAllocator.h
Normal file
|
@ -0,0 +1,149 @@
|
|||
/********************************************************************************
|
||||
* ReactPhysics3D physics library, http://www.reactphysics3d.com *
|
||||
* Copyright (c) 2010-2018 Daniel Chappuis *
|
||||
*********************************************************************************
|
||||
* *
|
||||
* This software is provided 'as-is', without any express or implied warranty. *
|
||||
* In no event will the authors be held liable for any damages arising from the *
|
||||
* use of this software. *
|
||||
* *
|
||||
* Permission is granted to anyone to use this software for any purpose, *
|
||||
* including commercial applications, and to alter it and redistribute it *
|
||||
* freely, subject to the following restrictions: *
|
||||
* *
|
||||
* 1. The origin of this software must not be misrepresented; you must not claim *
|
||||
* that you wrote the original software. If you use this software in a *
|
||||
* product, an acknowledgment in the product documentation would be *
|
||||
* appreciated but is not required. *
|
||||
* *
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be *
|
||||
* misrepresented as being the original software. *
|
||||
* *
|
||||
* 3. This notice may not be removed or altered from any source distribution. *
|
||||
* *
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef REACTPHYSICS3D_HEAP_ALLOCATOR_H
|
||||
#define REACTPHYSICS3D_HEAP_ALLOCATOR_H
|
||||
|
||||
// Libraries
|
||||
#include "configuration.h"
|
||||
#include "MemoryAllocator.h"
|
||||
#include <cassert>
|
||||
#include <containers/Map.h>
|
||||
|
||||
/// ReactPhysics3D namespace
|
||||
namespace reactphysics3d {
|
||||
|
||||
// Class HeapAllocator
|
||||
/**
|
||||
* This class is used to efficiently allocate memory on the heap.
|
||||
* It is used to allocate memory that cannot be allocated in a single frame allocator or a pool allocator.
|
||||
*/
|
||||
class HeapAllocator : public MemoryAllocator {
|
||||
|
||||
private :
|
||||
|
||||
// -------------------- Internal Classes -------------------- //
|
||||
|
||||
// Structure MemoryUnitHeader
|
||||
/**
|
||||
* Represent the header of a memory unit in the heap
|
||||
*/
|
||||
struct MemoryUnitHeader {
|
||||
|
||||
public :
|
||||
|
||||
// -------------------- Attributes -------------------- //
|
||||
|
||||
/// Size in bytes of the allocated memory unit
|
||||
size_t size;
|
||||
|
||||
/// True if the memory unit is currently allocated
|
||||
bool isAllocated;
|
||||
|
||||
/// Pointer to the previous memory unit
|
||||
MemoryUnitHeader* previousUnit;
|
||||
|
||||
/// Pointer to the next memory unit
|
||||
MemoryUnitHeader* nextUnit;
|
||||
|
||||
/// True if the next memory unit has been allocated with the same call to malloc()
|
||||
bool isNextContiguousMemory;
|
||||
|
||||
// -------------------- Methods -------------------- //
|
||||
|
||||
MemoryUnitHeader(size_t size, MemoryUnitHeader* previousUnit, MemoryUnitHeader* nextUnit, bool isNextContiguousMemory)
|
||||
: size(size), isAllocated(false), previousUnit(previousUnit),
|
||||
nextUnit(nextUnit), isNextContiguousMemory(isNextContiguousMemory) {
|
||||
|
||||
assert(size > 0);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// -------------------- Constants -------------------- //
|
||||
|
||||
static size_t INIT_ALLOCATED_SIZE;
|
||||
|
||||
// -------------------- Attributes -------------------- //
|
||||
|
||||
/// Base memory allocator
|
||||
MemoryAllocator& mBaseAllocator;
|
||||
|
||||
/// Allocated memory (in bytes)
|
||||
size_t mAllocatedMemory;
|
||||
|
||||
/// Pointer to the first memory unit of the linked-list
|
||||
MemoryUnitHeader* mMemoryUnits;
|
||||
|
||||
/// Pointer to a cached free memory unit
|
||||
MemoryUnitHeader* mCachedFreeUnit;
|
||||
|
||||
// TODO : REMOVE THIS
|
||||
Map<size_t, int> mDebug;
|
||||
|
||||
#ifndef NDEBUG
|
||||
/// This variable is incremented by one when the allocate() method has been
|
||||
/// called and decreased by one when the release() method has been called.
|
||||
/// This variable is used in debug mode to check that the allocate() and release()
|
||||
/// methods are called the same number of times
|
||||
int mNbTimesAllocateMethodCalled;
|
||||
#endif
|
||||
|
||||
// -------------------- Methods -------------------- //
|
||||
|
||||
/// Split a memory unit in two units. One of size "size" and the second with
|
||||
/// left over space. The second unit is put into the free memory units
|
||||
void splitMemoryUnit(MemoryUnitHeader* unit, size_t size);
|
||||
|
||||
// Merge two contiguous memory units that are not allocated.
|
||||
void mergeUnits(MemoryUnitHeader* unit1, MemoryUnitHeader* unit2);
|
||||
|
||||
public :
|
||||
|
||||
// -------------------- Methods -------------------- //
|
||||
|
||||
/// Constructor
|
||||
HeapAllocator(MemoryAllocator& baseAllocator);
|
||||
|
||||
/// Destructor
|
||||
virtual ~HeapAllocator() override;
|
||||
|
||||
/// Assignment operator
|
||||
HeapAllocator& operator=(HeapAllocator& allocator) = default;
|
||||
|
||||
/// Allocate memory of a given size (in bytes) and return a pointer to the
|
||||
/// allocated memory.
|
||||
virtual void* allocate(size_t size) override;
|
||||
|
||||
/// Release previously allocated memory.
|
||||
virtual void release(void* pointer, size_t size) override;
|
||||
|
||||
/// Reserve more memory for the allocator
|
||||
void reserve(size_t sizeToAllocate);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -31,7 +31,8 @@ using namespace reactphysics3d;
|
|||
// Constructor
|
||||
MemoryManager::MemoryManager(MemoryAllocator* baseAllocator) :
|
||||
mBaseAllocator(baseAllocator == nullptr ? &mDefaultAllocator : baseAllocator),
|
||||
mPoolAllocator(baseAllocator == nullptr ? mDefaultAllocator : *baseAllocator),
|
||||
mSingleFrameAllocator(baseAllocator == nullptr ? mDefaultAllocator : *baseAllocator) {
|
||||
mHeapAllocator(*mBaseAllocator),
|
||||
mPoolAllocator(mHeapAllocator),
|
||||
mSingleFrameAllocator(mHeapAllocator) {
|
||||
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
// Libraries
|
||||
#include "memory/DefaultAllocator.h"
|
||||
#include "memory/PoolAllocator.h"
|
||||
#include "memory/HeapAllocator.h"
|
||||
#include "memory/SingleFrameAllocator.h"
|
||||
|
||||
/// Namespace ReactPhysics3D
|
||||
|
@ -40,7 +41,11 @@ class MemoryAllocator;
|
|||
// Class MemoryManager
|
||||
/**
|
||||
* The memory manager is used to store the different memory allocators that are used
|
||||
* by the library.
|
||||
* by the library. The base allocator is either the default allocator (malloc/free) of a custom
|
||||
* allocated specified by the user. The HeapAllocator is used on top of the base allocator.
|
||||
* The SingleFrameAllocator is used for memory that is allocated only during a frame and the PoolAllocator
|
||||
* is used to allocated objects of small size. Both SingleFrameAllocator and PoolAllocator will fall back to
|
||||
* HeapAllocator if an allocation request cannot be fulfilled.
|
||||
*/
|
||||
class MemoryManager {
|
||||
|
||||
|
@ -52,6 +57,9 @@ class MemoryManager {
|
|||
/// Pointer to the base memory allocator to use
|
||||
MemoryAllocator* mBaseAllocator;
|
||||
|
||||
/// Memory heap allocator
|
||||
HeapAllocator mHeapAllocator;
|
||||
|
||||
/// Memory pool allocator
|
||||
PoolAllocator mPoolAllocator;
|
||||
|
||||
|
|
|
@ -99,6 +99,13 @@ void ContactSolverSystem::init(List<ContactManifold>* contactManifolds, List<Con
|
|||
warmStart();
|
||||
}
|
||||
|
||||
// Release allocated memory
|
||||
void ContactSolverSystem::reset() {
|
||||
|
||||
if (mAllContactPoints->size() > 0) mMemoryManager.release(MemoryManager::AllocationType::Frame, mContactPoints, sizeof(ContactPointSolver) * mAllContactPoints->size());
|
||||
if (mAllContactManifolds->size() > 0) mMemoryManager.release(MemoryManager::AllocationType::Frame, mContactConstraints, sizeof(ContactManifoldSolver) * mAllContactManifolds->size());
|
||||
}
|
||||
|
||||
// Initialize the constraint solver for a given island
|
||||
void ContactSolverSystem::initializeForIsland(uint islandIndex) {
|
||||
|
||||
|
|
|
@ -382,6 +382,9 @@ class ContactSolverSystem {
|
|||
/// Solve the contacts
|
||||
void solve();
|
||||
|
||||
/// Release allocated memory
|
||||
void reset();
|
||||
|
||||
/// Return true if the split impulses position correction technique is used for contacts
|
||||
bool isSplitImpulseActive() const;
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user