I am sure, like me a lot of programmers here have struggled with problems with their allocator from time to time. What has really shocked me is that the allocator tends to be neglected in favour of other systems. I’d estimate 50% of the companies I have worked for have had (or still have) a sub par allocator at some point. 25% still used the allocator provided by the compiler. This might seem quite a controversial statement to make, but look a little closer and you may find yourself agreeing with me. We are always looking at the graphics engine and other big, ‘important’ systems and neglect the most important one of all. Although it is quite small an allocator does the biggest job of them all.
I formed Jury Rig Software because of this. Although we make an allocator called Elephant Memory Manager, this article is not specific to our product. There are lots of choices available ranging from a few commercial products, such as our own, open source or writing your own.
In this article I’m going to cover what I think you should have as a minimum in a game allocator.
Do you have a custom allocator?
A simple question but one I have to ask. Most people in games will be shouting yes to this and swiftly move on to the next section. A few of you will say no or mumble something about not needing one.
If you don’t have even a simple one, I’d argue that you have no idea what is going on with your memory internally. I would also expect you, even on a fairly small project, to have several unknown memory leaks and for it to take a long time to track bugs.
Nowadays it is pretty much impossible to get away from dynamic allocations. Even well-known middleware allocates dynamically.
Is your custom (or nonexistent) allocator up to the job?
I have lost count over the years of how often I have needed data on memory and not been able to get it. The data I have been able to get has sometimes been so poor I’ve been forced to add my own features to those provided by the allocator just to get the information that I need.
You have to ask yourself if the allocator in use on your game is really up to the job. If you answered no to the first question then the answer here is a definite no. If you have your own custom allocator it’s time to decide if it really does do everything you need or may need it to do.
The basics you should have in a custom allocator
1. Alignment: Absolutely critical these days. The minimum you should always look at supporting is 16 bytes. That can seem quite large you may be tempted to say that 8 is perfectly feasible but the alignment should match the largest type and that is nearly always 16 bytes. All the major manufacturers, except Nintendo, default to this for a good reason. On the Wii the default is 32 bytes. Your allocator also needs to deal with large allocation as well. Some platforms require sizes of 1MB or more for hardware or performance reasons.
2. Logging: You must be able to log every allocation. Logging is one of the most useful methods of tracking errors, especially leaks.
Even if it is just output to a CSV file that can be loaded in Excel, it is important that you are able to log your memory. Goldfish , the tool that comes with Elephant, will load these to help visualize your memory usage. The better the logging the better you can visualize your memory.
3. Usage: Being able to monitor usage is integral. The number of active allocations can help spot basic memory leaks. Total amount allocated, high water marks etc are some of the most useful basic features. You can display this in real-time and really see what is going on.
4. Thread safety: There are one or two allocators out there that don’t provide this at all. Make sure the one you use isn’t one of these. Even single threaded platforms can have threading problems, so make sure this doesn’t catch you out. Just because you can guarantee that your project has just the one thread, doesn’t mean the libraries you call don’t create extra threads.
5. Warning/Error reporting: Break early and break often is a golden rule. Definitely break as soon as an error is picked up. Many people do not check for null memory conditions, invalid addresses, duplicate frees. These can wreak havoc with the allocators structures causing almost impossible to track down errors.
If your allocator is able to do these five things then you have a firm basis for managing your memory. Additional features can improve performance and aid bug tracking still further; however with these five you should be able to track the most common problems quickly and easily.
In my next post I will cover more advanced features and some other points that can help with multi platform development and finding the trickier bugs that can appear.