Measuring can be fun!

I was lately asked if I had ever done a breakdown on the memory usage in Aeternum (via Daniel Dobson).

The answer is a hearty and emphatic yes. I’ve been watching memory usage on Aeternum from the very beginning, because I had a very strong hypothesis that managing upwards of 2000 scripted collision particles (aka bullets) on screen simultaneously could be a potential bottleneck. So memory has been a concern of mine from the beginning with this project.

The best tool for the XNA platform for keeping tabs on these problems is of course, the .NET CLR Profiler. Every XNA developer out there that’s used it knows what I’m talking about, and to everyone who hasn’t, I can only say “Learn to utilize the profiler, and you’ll love it.”

So here’s a snapshot of a brief amount of time somewhere in stage 1 of Aeternum, as viewed by the memory allocations portion of the profiler.

 

This graphic points out three strategies I’ve used to help avoid unnecessary garbage collection overhead. To put it in scale, that entire heap is about 2 MB

1. Identify assets that persist throughout the entire game, load them and keep them.

You’ll notice very early on I load in a few assets that persist through the lifetime of the game. ¬†This is mostly for fonts, some UI elements, and player character assets. No reason to throw them out if they’re just going to always be reloaded a second later.

2. Adopt a very aggressive pooling strategy.

The most important aspect in the case of Aeternum was to develop techniques for pooling entities. Particles, bullets and powerups are all initialized early on, and recycled. The fact these discrete components are each in contiguous parts of memory probably doesn’t hurt when it comes to accessing them for updates every frame either.

3. Learn when to stop caring about garbage.

I read a lot of developers who stress about garbage collection. And yes, if you make a few mistakes, do too many unnecessary allocations or forget to release references for example garbage collection can destroy your frame rate.¬†However the strategies I have adopted for managing long term vs potentially volatile assets have mitigated the bulk of this problem in my case. This has left me in the position of being able to create my stage content without really thinking too much about dealing with collection problems. Looking at the graphic again, you’ll see at the top all the junk that had built up on the heap over the course of the stage. Most of that had already gone out of scope by this point and is ready to be collected. In actual practice the heap garbage that builds up over the course of the stage usually doesn’t ever trigger a collection. Instead I hold off until a stage change and initiate a collection manually, leaving the persistent components in memory and cleaning up the rest of the space for the new stage. I also hide the frame hiccup there because the screen fades to black between stages anyway.

In conclusion, I can’t definitively say if these are best practices or not. I can only say that from what I’ve seen, these things have worked for me. The game runs great, even with all the bullets and particles flying around.

  • Great Read :) Using Pools is great for being contiguous as you say, and actually leads towards a cleaner design I find, because you know how many active bullets/entities/vfx you can have at any one time, and so re-use is promoted.

    • Thanks. The hard part was actually building the bullet class itself to be poolable to begin with. But I’ll save the implementation details of my bullet engine for a later post, as I think it’s the most interesting part of the whole code base.

      • I’m guessing you needed to do some kindof Reset() to make it feel like it has just been constructed. And then remember to add all new member data to the Reset() if the class grows :) Those are the main gotchas with pools that I’m aware of.

  • By pools, do you mean something like an object factory? That is it say creates some shots, then if more are needed, it makes more, and they are all reused during the game? Or something like that?

    • Excellent question. Maybe I should write another article to go in depth on pools themselves.

      When it comes to something like this, “creating new” instances during run time is about the last thing you want to do. Instantation costs processor time and memory that you might not be able to spare. And often it means you’re leaving garbage out to be collected which can lead to even worse problems.

      So, by “pooling” I mean, at the very beginning of the game, during loading, I pre-allocate a large number of bullet instances, powerup instances, and particle instances. Enough to be never have to make a new one while the game itself is running.

      From that point, every time I need a bullet/particle/powerup, I pull in one of the unused, already allocated instances, fill it up with data as necessary, and use it til it’s no longer needed. Then it goes back into a list, still an allocated instance, just unused again.

      The upshot is that the costly allocation is all done up front, and you never have any garbage collection because you never actually remove all references to the object. You keep them all in a “pool” for usage.

about

WastedBrilliance is an independent video game development studio run by Brooks Bishop. With contributions by Nate Graves, Jesse Bishop and Geoff Schultz.