Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I have been interesting in profiling and keeping a managed memory pool for each subsystem, so I could get statistic on how much memory was being used in something such as sounds or graphics. However, what is the best design for doing this? I was thinking of using multiple allocators and just using one per subsystem, however, that would result in global variables for my allocators (or so it would seem to me). Another approach I have seen/been suggested is to just overload new and pass in an allocator for a parameter.

I had a similar question over on stackoverflow here with a bounty, however, it seems as if perhaps I was too vague or just there is not enough people with knowledge in the subject.

share|improve this question
    
Removed my answer. I went back and read the articles and the pools used to track per-system allocations are not part of those articles (even though they were based on it) If I can find those specific ones again I will link those instead! Sorry! –  James Mar 16 '12 at 3:54
1  
Take a look at this blog post by Jesus de Santos Garcia. In it, he discusses tracking memory by subsystem, and using multiple allocators for various memory requirements. –  K.G. Mar 26 '12 at 3:00
6  
Don't get obsessed with theoretical paradigms. If you need functionality and data globally, then there is nothing wrong with globals. There are already a bunch of global functions like new/delete/malloc/free. Just add what you have to, to get the job done. –  Maik Semder Nov 1 '12 at 16:45
add comment

3 Answers

Its definitely a question that could sound vague to some ;)

But I think I know where you are coming from.

You have a million choices for how you could choose to implement this. Some of those choices should revolve around both the target platforms and the overall design goals. Those considerations will break any ties, until you feel comfortable enough with different implementation costs enough to grow the design from the platform and general design concerns first. So until then, here are some ways that wont cost you in terms of complexity (management burden) or in dealing with removal or changes if you change your mind...

If the goal is to measure and allocate, with the possibility of using pools, then you need to think first bout the minimum set of livable code to get started. For the sake of explanation, if you are partial to classes, you could make one class, an let that stand for a heap, or instead use a set of functions that take a handle or a heap name. Its really an issue of semantics to be honest. Next decision is new or malloc; I'm partial to malloc because many times I'm dealing with low level constructs and I know in most implementations that new calls malloc, and I don't have to worry about the complexity of overloading new, and worrying about that on all platforms. However I have many times built systems or components around overloading or hooking new. And of course the core problem or difference, is that 'new' must know the type before the allocation, where as 'malloc' doesn't care and with malloc you resolve to a type after the allocation. All that detail is to give you an idea and some context for making design decisions in these types matters :)

So I'm going to pick class and malloc, because its easier to explain here, but it really doesn't matter in the end. The internals will end up bearing little material difference as compared to the rest of the overall design.

So in this hypothetical, I know that (or going to assume that) i may end up with 7-8 real sub-system class instantiations, and anticipate 100s of thousands of calls for allocation and free. Since much of my curiosity and real thrust in all this, is really about the sizes and profile side, I don't want to burden the the app performance wise. For starters I might decide to just leave the whole thing open and public until I have it nailed down, as I walk through implementing it throughout the rest of the app; a struct will do this nicely. The 's_' is to show which vars are clearly intended for statistics.

struct Mem
{
  int s_allocs;
  int s_frees;
  int s_peak;
  int s_current;
  void* heap; // if you wanted to go into having real actual separate heaps, else ignore
  void* alloc(int size);
  void free(void* p);

  Mem() {memset(this,0,szieof(Mem));}  // want this to be inlined with the call site constructor (a design decision example)
}

class MySubSystem
{
   Mem mem;
   ....  you get the idea
}

This is extremely lightweight on many fronts, and maybe a good place to start to flesh out where ever you really wanted to go with this. And you immediately have a problem, how do you know the size of freed item. (This would be a problem to solve for nearly any approach.) Since this is a game forum you could consider doping the first few bytes with the size, or else you have to either wrap or remember in some other way. Most game dev sensibilities shouldn't be too much against the doping, and its the simplest example, considering I've already made a wall of text. Basically goes like this: you don't want if can be helped to destroy the inherent alignment, you do want to know, since almost for free, if the size is coherent. So something as simple as "s_allocs++; s_total+=size; uint64* p = (uint64*)malloc/calloc(size += 8); *p = 0xDEADDAED00000000 | size; return p + 1;" where allocations will be less than 4GB, and uint64 is whatever the compiler thinks a 64 bit unsigned int is, and where you can check the sanity value on free.

This is all one way to get you the bare minimum at a bare minimum cost that satisfies the requirements. It does not address allocating classes that have virtual functions if those are in scope for profiling or management, as you cant anticipate the size the c++ environment you are using needs for those without overloading or hooking new, or if you are relying on the constructor in one of the odd ways that couldn't be handled by some other 'init' function. Otherwise a stuct is a class is a arbitrary alloc and is all the same, when you cast. If you are partial to new and need the inherent virtual table or constructor semantics, then you must hook new, but that's a whole 'nother animal, which you need to really study to make sure you are doing what new needs and would have to signal your code thats handling new, which bucket this applied to. But otherwise the above concept is the same.

More importantly this should get your brain going, and hopefully along the lines of what you need and what your tolerances are, now that you've seen behind the curtain a little more. There is no wizard :)

share|improve this answer
    
If you were using template functions for allocating and freeing it shouldn't be a problem to determine the size needed to be freed (and allocated). –  Mr. Beast Sep 2 '12 at 13:20
1  
The point was to show the underlying problem in order to help provide a basis to choose whatever abstraction, not to present a particular abstraction. Everything has its tradeoffs. The knowedge of the size is necesary not to do the deed of freeing, but for statistics. –  Celess Sep 3 '12 at 6:33
add comment

You don't need to implement anything in your game for this data. Tools such as Massif Valgrind can extract all necessary data from the Debug Symbols. You can view the dumps of Massif in Massif Visualizer.

share|improve this answer
4  
I would guess that most graphical games are not going to be developed on a system that runs Valgrind, unfortunately. –  Kylotan Nov 1 '12 at 17:25
add comment

I highly recommend not to write your own memory allocator. You need a stable, reliable and tested one, with good debugging functionality like corruption detection and most importantly: with reliable statistics. This is not an easy task and has many pitfalls. There are great and easy to utilize ones out there, for instance:

Doug Lea Allocator

It comes with the concept of memory spaces, you can use one per subsystem. It's highly optimized and gives you great statistics and runtime-information.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.