Main Page   Modules   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members  

MemAlloc Class Reference

A fast (de)allocator of fixed-size memory chunks. More...

#include <MemAlloc.h>

Inheritance diagram for MemAlloc

Inheritance graph
[legend]
Collaboration diagram for MemAlloc:

Collaboration graph
[legend]
List of all members.

Public Methods

 MemAlloc (ppSizeT unitSize, ppu32 blockSize = 16)
 Notice that MemAlloc does not have a default constructor. More...

 ~MemAlloc ()
 The destructor returns all allocated memory to the free store. All pointers returned by Allocate() is made invalid by this.

void* Allocate ()
 Allocate() returns a chunk of memory of the size specified as the parameter unitSize in either the constructor or Reset(). More...

void Deallocate (void* pAdr)
 Deallocate() marks the chunk of memory pointed to by pAdr as free. More...

void RemoveExcessCapacity ()
 If a very large amount of units have been allocated using MemAlloc, MemAlloc will have had to allocate a proportionally large amount of memory from the free store. More...

void* RemoveExcessCapacity (void* pProgress, ppSizeT increments = 1)
void Reset (ppSizeT unitSize = 0, ppu32 blockSize = 0)
 Reset() returns all allocated memory to the free store. More...


Protected Methods

void MemoryUnderflow ()
 Method called by Allocate() when it finds that the linked list of free units is empty. More...

void* RawIncrement (void* pAdr, ppSizeT incrementBy)
 This is a utility method used several places internally in MemAlloc to increment a pointer without using the special pointer arithmetic rules of C++.


Protected Attributes

ppSizeT m_unitSize
 The size of the chunks of memory returned by Allocate().

ppu32 m_blockSize
 The amount of chunks of memory of the size specified by m_unitSize that is contained in each block. More...

Nodem_pBlockListHead
 A pointer to the first block in the linked list of allocated blocks. More...

Nodem_pFreeListHead
 A pointer to the first unit in the linked list of free units. More...


Detailed Description

A fast (de)allocator of fixed-size memory chunks.

The MemAlloc class is an allocator that is several times faster than the standard memory allocator both at allocation and deallocation.

This is accomplished by having some restrictions on how it can be used. MemAlloc can only allocate units of memory of a specified size, and this size has to be specified at construction and/or reinitialization.

More specificly, the allocator allocates blocks large enough to hold a specified amount of memory units that are of a specified size. One of these units can then be returned when the allocator is asked to allocate a unit instead of calling new. The block size, the amount of units per block, can only be set at construction and/or reinitialization.


Constructor & Destructor Documentation

MemAlloc::MemAlloc ( ppSizeT unitSize,
ppu32 blockSize = 16 )
 

Notice that MemAlloc does not have a default constructor.

unitSize is the size of the units that the allocator will be allocating.

blockSize is the number of units that the allocator allocates with new() at a time. This size doesn't have as big an impact on performance as it migth seem, though it does matter. The default size of 16 should meet most needs.

The constructor and Reset() is the only two places in which unitSize and blockSize can be set.


Member Function Documentation

void * MemAlloc::Allocate ( )
 

Allocate() returns a chunk of memory of the size specified as the parameter unitSize in either the constructor or Reset().

When changing code to use MemAlloc instead of new and delete, or malloc() and free(), replace all calls to new with a call to this method.

In cases where Allocate() needs to allocate more memory with new as a result of running out of capacity, Allocate() migth result in an exception of type bad_alloc, as it calls new.

void MemAlloc::Deallocate ( void * pAdr )
 

Deallocate() marks the chunk of memory pointed to by pAdr as free.

Only call Deallocate with a pAdr argument that has previously been returned by Allocate, and only once for each call to Allocate. If this guideline is not followed, MemAlloc will behave in an undefined manner.

Notice that calling Deallocate will not cause the chunk of memory pointed to by the pAdr argument to be returned to the free store, but rather serve as more capacity for MemAlloc.

When changing code to use MemAlloc instead of new and delete, or malloc() and free(), replace all calls to delete with a call to this method.

void MemAlloc::MemoryUnderflow ( ) [protected]
 

Method called by Allocate() when it finds that the linked list of free units is empty.

This method might result in an exception of type bad_alloc, as it calls new.

void MemAlloc::RemoveExcessCapacity ( ) [inline]
 

If a very large amount of units have been allocated using MemAlloc, MemAlloc will have had to allocate a proportionally large amount of memory from the free store.

This memory is not returned to the free store, even if all pointers returned by Allocate() have been called as the argument of Deallocate().

In situations where alot of memory has been allocated, and a client of MemAlloc is in a situation where it makes sense to have the allocated memory returned to the free store, then this client should call RemoveExcessCapacity().

The overload of RemoveExcessCapacity() that takes no arguments checks all allocated blocks of memory to see wheter all the units in each block has been deallocated. Any block in which all units have been dealloccated is returned to the free store.

The overload of RemoveExcessCapacity() that takes 2 arguments does the same thing as the overload that takes no arguments, but it only checks a specified amount of blocks, thus solving only a sub-part of the larger problem in each invocation.

The first parameter, pProgress, should be set to 0 to initiate the process and tell RemoveExcessCapacity() to start checking from the first block.

RemoveExcessCapacity returns a void pointer. If this pointer is 0, it means that RemoveExcessCapacity() has checked all allocated blocks of memory, including those allocated after the initial call to RemoveExcessCapacity(). If the returned pointer is not 0, then this value should be passed as the first argument, pProgress, when calling RemoveExcessCapacity() the next time. This will allow RemoveExcessCapacity() to pick up where it stopped in the previous invocation.

The second and last argument, increments, which defaults to 1, specifies how many blocks of allocated memory to check before returning. An increments argument of -1 means that RemoveExcessCapacity() should check all remaining blocks of memory.

Calling the overload of RemoveExcessCapacity() with two arguments with a first pProgress argument of 0, and a second increments argument of -1 is functionally equal to calling the parameterless overload of RemoveExcessCapacity().

Notice that RemoveExcessCapacity is an O(n-squared) method, where n is the amount of allocated units in MemAlloc that is free. This means that when extremely large amounts of allocated blocks exist, but there is not alot of the allocated units that are available as a result of this that have been allocated, RemoveExcessCapacity() can take longer to run than one migth expect.

To remedy this, the overload of RemoveExcessCapacity() that takes two arguments makes it possible to only compute a sub-part of the problem in each invocation, thus making it possible for RemoveExcessCapacity() to be used in idle-time.

Neither overload of RemoveExcessCapacity() allocates any memory, so this method might be a good candidate when searching for ways of handling out-of-memory situations.

void MemAlloc::Reset ( ppSizeT unitSize = 0,
ppu32 blockSize = 0 )
 

Reset() returns all allocated memory to the free store.

All pointers returned by Allocate() prior to this is made invalid by this.

unitSize is the size of the units that the allocator will be allocating.

blockSize is the number of units that the allocator allocates with new() at a time. This size doesn't have as big an impact on performance as it migth seem, though it does matter. The default size of 16 should meet most needs.

The constructor and Reset() is the only two places in which unitSize and blockSize can be set.

If the arguments unitSize or blockSize is allowed to have its default value, 0, then the unitSize and blockSize that MemAlloc had prior to the invocation of Reset() is preserved.


Member Data Documentation

ppu32 MemAlloc::m_blockSize [protected]
 

The amount of chunks of memory of the size specified by m_unitSize that is contained in each block.

Notice that each block is 4 or 8 bytes larger than m_blockSize * m_unitSize. The reason for this is that each block has a head which stores a pointer to the next node in the list of blocks.

MemAlloc is 32-bit aligned by default. In this case, heads are 4 bytes large; exactly the size needed for the pointer. If 64-bit alignment is specified, then heads need to be 8 bytes, consuming 4 extra bytes of memory to attain proper alignment.

Node * MemAlloc::m_pBlockListHead [protected]
 

A pointer to the first block in the linked list of allocated blocks.

The nodes of this linked list is stored in the first 4 bytes of the head of each block.

Node * MemAlloc::m_pFreeListHead [protected]
 

A pointer to the first unit in the linked list of free units.

The nodes of this linked list is stored in the memory of the free units themselves. This is ok since this memory is by definition not used for anything else since it is free.

This is the reason that MemAlloc cannot have a unit size less than 4; there has to be room for the nodes in the list of free units.


The documentation for this class was generated from the following file:
Generated at Mon Nov 27 01:47:01 2000 for LibPenguinPlay by doxygen1.2.3 written by Dimitri van Heesch, © 1997-2000