One of the things I enjoy about C in comparison to C++ is its simplicity. You rarely have to worry about what’s going on behind the scenes — the compiler isn’t going to play any dirty tricks on you with copy constructors or memory layouts. It’s pretty much a what-you-code-is-what-you-get situation. Even memory management is much simpler. Implementing custom memory managers in C++ can be tricky if you are making use of the STL or another library that overrides new and delete.
As a coding exercise a while back, I took a custom memory tracker from Peter Dalton’s Game Programming Gems 2 article, “A Drop-in Debug Memory Manager,” and ported it to C. The end result wasn’t too much different from the original, but I was able to cut out some of the cruft the C++ version required. I was quite happy with it and have since put it to work in some of my little “research” projects.
In C++, the conventional way to direct memory allocation to a custom allocator is to override new and delete. Since it isn’t possible to override malloc and free in C, you must resort to either wrapping them. Then, you can use macros as a poor man’s override. For example:
#define malloc MyMalloc
#define free MyFree
The above defines will do the trick. Of course, you can make your wrappers a bit more useful:
#define malloc(s) MyMalloc(__FILE__, __LINE__, (s))
#define free(p) MyFree(__FILE__, __LINE__, (p))
There’s nothing wrong with defining ‘malloc’ and ‘free’ macros to override the standard library functions, but I prefer not to. I use macros with custom names instead. It’s primarily a matter of style. There is a possibility of problems arising from the overrides when you include third-party headers, but it’s a one-in-a-million sort of thing. So if you do choose to use ‘malloc’ and ‘free’ overrides rather than custom names, you should be just fine.
Another thing to consider — something that is important in game programming, not just with C but also when using C++ — is that you can implement different allocators for different builds of your application. During most of the development process, you’ll probably want to use a ‘default’ allocator that just wraps the standard library functions and does nothing else. Then, you might want to use a lightweight tracker, like the Dalton code I mentioned above, to track memory leaks and some general allocation statistics at different points during development. During the final stages, you might want to implement a more heavy duty tracker that can help you reduce fragmentation, implement optimized allocation strategies, and any other information you need to fine tune your memory usage. You may even want to implement a garbage collector. Using conditional defines, you can build any allocator you want:
#if defined(MEM_DEFAULT)
#define MyMalloc(s) MyMalloc_Default((s))
#define MyFree(p) MyFree_Default((p))
#elif defined(MEM_DALTON)
#define MyMalloc(s) MyMalloc_Dalton(__FILE__,__LINE__,(s))
#define MyFree(p) MyFree_Default(__FILE__,__LINE__,(p))
#endif
Many popular C libraries that you might use for game development allow you to specify allocators and deallocators that the library will use instead of malloc and free. This means you can plug your custom allocators in to such libraries to get more control and more accurate statistics.
If you aren’t tracking your memory usage patterns, you really need to be. There are professional tools out there that can spit out a lot of details about it for you, but if you don’t have the money, or don’t want to spend it, a custom implementation can take you a long way. Using macros like the above will help you plug your allocators into your app, but implementing a useful memory tracker is no walk in the park. For ideas on how to get started, I direct you to the Game Programming Gems 2 article by Peter Dalton that I mentioned above. It’s a simple memory tracker, but can help point you in the right direction.
Technorati Tags: C, C++, memory management, tips & tricks
{ 1 } Trackback
[…] One of the things GameDevMike enjoys about C in comparison to C is its simplicity. You rarely have to worry about what’s going on behind the scenes — the compiler isn’t going to play any dirty tricks on you with copy constructors or memory layouts. It’s pretty much a what-you-code-is-what-you-get situation. Even memory management is much simpler. Implementing custom memory managers in C can be tricky if you are making use of the STL or another library that overrides new and delete. […]
Post a Comment