I have been researching creating my own allocator methods (that will support things such as a memory pool and profiling), however, as I continue my research I have been looking for how this was done in game development. Are there any "go to" examples of allocators used in game development, and if so, what are the advantages of them (if known).
Game Engine Architecture has some information regarding this topic. The basics are that you need to do some analysis to understand what your memory requirements per level/frame/etc. are like, but there are a few patterns the author mentions having seen several times:
The biggest thing the author mentions to look out for is memory fragmentation. This is less of an issue if you're developing for e.g. a PC where you have some kind of memory paging backup that you can count on, but in a fixed memory context like a console, there's the risk of being "out of memory" when trying to allocate for a large object because your memory is fragmented in such a way that only small contiguous blocks are available. To that end, he recommends that a stack-based allocator as above also include a method of periodically defragmenting its contents.
For more information on the actual code involved in this, I highly recommend Christian Gyrling's article, "Are we out of memory?", which covers techniques for custom allocators, mostly from a perspective of analyzing memory usage patterns, but this is also applicable to devising a custom solution for memory management.
The mistake that is often made is to write your own allocators so that you can have more control over how much memory is used by each system and have more visibility on what is going on. A much better way to achieve this is to use a memory profiler. There are plenty of memory profilers out there, my profiler MemPro being one example. This is a totally non-invasive way to keep track of all memory usage, and you can automatically break it down into sub systems using callstack wildcard filters. Ideally its best to keep your memory allocation and memory tracking totally separate, they have totally different requirements.
Arbitrarily dividing your memory into pools can often be detrimental because each pool will have an overhead. You can end up using much more memory than you need without actually realising it. To reduce wastage it’s always better to lump everything together, the slack is then shared by the whole system.
The only reasons for using custom allocators are CPU performance (mainly for cache coherency) and to limit fragmentation. A perfect example of this is a particle system. You want all particles contiguous in memory and you don’t want to pepper main memory with lots of short lived allocations. Another good example for partitioning off is a scripting language.
If you want an example of a general purpose malloc replacement you can have a look at my VMem allocator. It has been used in a number of shipped AAA games. It has techniques that limit fragmentation and keep the memory footprint low, something critical for console games. It’s also very fast under high thread contention. My website has extensive documentation on these techniques.
From what I have seen (but not done) each game tends to either inherit the allocation mechanisms from a framework, from a game engine, from the previous version (2010 -> 2011) or it gets a set of new ones written specifically for its structure (either when data structures are reusable and of fixed size or of numerous types and variable sizes).
Also we had different allocators for sound files/components than for levels and other game objects in the same project. In other projects allocators are inherited from external libraries only for the components managed by that lib.
The optimization really depends on your needs. But usually allocation is done before entering to the game scene and then memory is reused. Some games can get away with gaving no custom allocators. But for action games where the processor, memory and data resources are budgeted you can't afford to lose processing time on large allocations, you can't waste memory to fragmentation and other problems.