Memory management is over

Here is another useless article about memory management.

During my spare time, I try to write reusable components for game development and the first one is a simple "memory management framework".

The main objectives are:
- Simplicity,
- Thread safety,
- Performances,
- Tweakability.

The first point is the most difficult one to manage since, like most programmers, I start with a simple design and at the end I create a monster: "Yes, he is alive ! alive !".

The first thing is to identify what will be allocations/deallocations entry points. I decided not to use C++ ::new and ::delete operators. I prefered to define my own templated allocation/deallocation functions:
- New<>,
- Delete<>,
- NewArray<>,
- DeleteArray<>,

All these functions are templated and I use Boost preprocessor library to automatically generate different version of New method with different number of parameters. Here is a sample code which uses this interface:

CMyObject * pObject = Memory::New<CMyObject>(1.5f);
Memory::Delete(pObject);

Note: Alignment is managed inside allocation functions thanks to C++ __alignof function.

Memory allocation/deallocation inside New<> methods is performed through other methods:
- VirtualAlloc,
- VirtualAlignedAlloc,
- VirtualFree,
- VirtualAlignedFree.

I lied a bit in the previous code sample. Indeed, New/Delete and VirtualAlloc/Free functions are embeded in classes:

class CDefaultAllocPolicy
{
      static void * VirtualAlloc (size_t Size);
      static void    VirtualFree (void * pPtr);
      static void * VirtualAlignedAlloc (size_t Size, size_t Alignment);
      static void    VirtualAlignedFree (void * pPtr);
};

template <typename TAllocPolicy = CDefaultAllocPolicy>
class CAlloc
{
     template <typename TType>
     static TType * New (void);

     template <typename TType>
     static void Delete (TType * pPtr);

     template <typename TType>
     static TType * NewArray (size_t ElemCount);

     template <typename TType>
     static void DeleteArray (TType * pPtr);
};

template CAlloc<>  TDefaultAlloc;

Source code is much more like this:

CMyObject * pObject = Memory::TDefaultAlloc::New<CMyObject>(1.5f);
Memory::TDefaultAlloc::Delete(pObject);

The ones who already developed multithreaded applications know that memory allocations could be an issue. Indeed, memory is a shared resource like others and concurrent allocations can generate huge slow down in your application. The straight forward solution is to use a synchronization mechanisms like semaphores, mutexes or critical sections but these ones are not the perfect solutions whether we want to be superlinear (http://herbsutter.wordpress.com/2008/01/30/effective-concurrency-going-superlinear/).

A common solution is to have multiple allocators and use Thread Local Storage to store the one which is currently used by a given thead. In my situation, allocators are memory Heaps (c.f. article about heap layers) which manage their own region of memory i.e. when I perform an allocation from one heap I don't need to block others.

Thread Local Storage is a commonly used trick in C++ to have some variables specific to a thread. On PC, you just have to use the directive __declspec(thread) e.g. :
__declspec(thread) unsigned int g_LocalStorage;

I have defined the following Thread specific structure:

struct SLocalStorage
{
        Memory::IHeap * m_pCurrentHeap;
        /* other data*/
};

and the following accessors:

SLocalStorage & GetLocalStorage (void);
void InitThread (void);
void ResetThread (void);

IHeap * SetCurrentHeap (IHeap * pHeap);
IHeap * GetCurrentHeap (void);

The code for Virtualloc/Free is quite simple:

void * System::Memory::VirtualAlloc (size_t    const AllocSize)
{
    Memory::IHeap * const pCurrHeap =  Memory::GetCurrentHeap();
    void * pMem = NULL;

    if (pCurrHeap)
    {
        pMem = pCurrHeap->Alloc(AllocSize);
    }
    else
    {
        pMem = __VirtualAlloc(AllocSize);
    }
    return (pMem);
}

void    System::Memory::VirtualFree (void * pBuffer)
{
    Memory::IHeap * const pCurrHeap =  Memory::GetCurrentHeap();

    if (pCurrHeap)
    {
        pCurrHeap->Free(pBuffer);
    }
    else
    {
        __VirtualFree(pBuffer);
    }
}


Note: __VirtualAlloc/Free are low level functions in charge of calling OS allocators i.e. malloc/free.

I have also implemented two helpers to perform allocations in a specific Heap. The first one is an object which follows Auto pattern to modify the current Heap for a given scope:

{
    AUTO_HEAP("HeapTest1");

    /* All New<>/Delete<> calls will go through HeapTest1 allocator */
}

The second one is a macro which declares Virtual alloc functions and New specializations for a given heap:

DECLARE_DOMAIN_NEW(12, Engine, "HeapTest1") ;

void MyFoo
{
    CMyObject * pObject = Memory::TEngineAlloc::New<CMyObject>(1.5f);
    Memory::TEngineAlloc::Delete(pObject);
}

I also implemented a Heap manager class in charge of gathering all heaps which are created.

That's all. Perhaps this memory management framework is a little bit too complex but it satisfies most of my needs ^^.

Now, since my memory management is over, I can start implementing more funny things .... let's implement a simple Virtual FileSystem

---
Manu


Article ajouté le 2009-06-07 , consulté 64 fois

Commentaires



Poster un commentaire





http://





Merci de recopier le nombre présent à gauche dans la case de texte ci-dessous ( Pourquoi ? )





Liens

Voir les articles de la catégorie " Programmation "

Retour aux articles



Recommander ce blog | Contacter l'auteur | Reporter un abus | S'abonner au blog Flux RSS du blog | Espace de gestion

Créer un blog gratuit avec Blog4ever