I’m working on a memory pool for a small game engine.
The main use will be as a segregated storage; a pool contains object of a specific type and size. Currently the pools can be used to store anything, but allocations will be done in blocks of a specific size. Most of the memory need will be allocated at once, but “overgrowth” can be enabled if needed to assist in tuning (almost fixed size).
Problem is, I started to get somewhat paranoid when contemplating about memory alignment. I’m only used to raw memory management on 8 bit processors where everything is byte aligned.
I’m letting the user (me) specify the desired size of the blocks, which in the segregated storage case would be the size of the objects that I’m going to store in it.
The current approach is to allocate a chunk of memory blocks * (desired_size + header_size) big and place the objects in it, with a header for each block; objects would obviously be positioned directly behind this header.
What do I need to consider with regards to memory alignment in my scenario?
The answer I’ve come up with so far is that as long as desired_size represents n-byte aligned data; the header is correctly aligned and packed by the compiler as well as the actual data, everything stored in the block will be n-byte aligned.
n is whatever boundary required by the platform. I’m targeting x86 for the moment but I don’t like to make any assumptions about the platform in my code.
Some of the resources I’ve used:
- http://www.ibm.com/developerworks/library/pa-dalign/
- http://en.wikipedia.org/wiki/Data_structure_alignment
- Memory alignment on a 32-bit Intel processor
- Boost Pool docs for inspiration on the general design. I would like to avoid dragging boost into this project; and I’m seeing this as a learning opportunity as well.
Edit
Uploaded small sample code which may helpful anybody as confused as me in the future here.
Allocations with
mallocare guaranteed to be aligned for any type provided by the compiler and hence any object[*].The danger is when your header has a smaller alignment requirement than the maximum alignment requirement for your implementation. Then its size might not be a multiple of the max. alignment, and so when you try to cast/use
buf + header_sizeas a pointer to something that does have the max. alignment, it’s misaligned. As far a C is concerned, that’s undefined behaviour. On Intel it works but is slower. On some ARMs it causes a hardware exception. On some ARMs it silently gives the wrong answer. So if you don’t want to make assumptions about platform in your code, you must deal with it.There are basically three tricks that I’m aware of to ensure that your header doesn’t cause misalignment:
intin as padding if necessary to make it an 8-multiple rather than just a 4-multiple”.unionof every standard type, together with the struct that you actually want to use. Works well in C, but you’d have problems in C++ if your header isn’t valid for membership of unions.Alternatively, you can just define
header_sizenot to besizeof(header), but to be that size rounded up to a multiple of some chunky power of 2 that’s “good enough”. If you waste a bit of memory, so be it, and you can always have a “portability header” that defines this kind of thing in a way that isn’t purely platform-independent, but makes it easy to adjust to new platforms.[*] with a common exception being over-sized SIMD types. Since they’re non-standard, and it would be wasteful to 16-align every allocation just because of them, they get hand-waved aside, and you need special allocation functions for them.