I’m just working on the code which will form the basis of my upcoming tutorial and it’s a huge learning experience for me. One thing I haven’t really focussed on before was optimisation of .NET code. While I’ve re-factored code because it was doing things in a ’roundabout’ way that could be optimised, I haven’t had to ever worry about real-time performance before. With XNA, a sudden pause or glitch is going to be bad for a game.
One thing that will cause glitches in XNA games is if the garbage collector kicks in at an inappropriate time so a strategy to avoid this needs to be adopted quite early on in development of your game engine.
An excellent post by Shawn Hargreaves gives an overview of two distinct strategies to reduce the impact of garbage collection on your code. By picking one of these and sticking to it, you can either ensure that when garbage collection occurs, it doesn’t affect performance adversely or avoid garbage collection altogether.
Approach 1
Don’t allocate anything. This sounds quite hard to achieve but it isn’t as difficult as it sounds. You can allocate memory still (your game will be pretty limited if you cannot) but you allocate it all up-front.
The challenge with allocating everything up-front is that you have to know before-hand in your game how many objects you will need exactly. Well that’s pretty straightforward for storing something like meshes as you will know at compile time how many you need. It’s when you want to show multiple instances of those meshes on-screen at runtime that you are faced with a problem.
For example, if you have a car model and want to show a street scene in your game, you may wish to re-use that car model to place 3 cars on the road at night but put 20 on the road at peak times. You’ll need to allocate enough of those objects during the games initialisation to deal with all possibilities.
Approach 2
Allocate what you want, whenever you want but keep the managed heap simple. Each time you create a new object and store the reference, that is one more reference the GC needs to keep track of. The more references you have, the longer GC takes – bad news during a screen update. A lot of data with plenty of references to other objects is going to slow things down come GC time and the fact you are constantly allocating means that GC will occur sooner or later – always when you least need it.
To keep references to a minimum, you could use arrays to store your object collections and use a simple integer reference to link them instead. See Shawn’s post for more details.
My choice?
I went for Approach 1 – don’t allocate mid-game. The main reasons were that Approach 2 required that I maintain references between objects using integer indexes to look-up a required object in a collection. That then means that I have to manage those references myself. I find that idea a little crazy as c# is more than capable of maintaining those references out-of-the-box and the idea that I would ditch that and implement my own reference management code sounds like a recipe for disaster! As soon as I destroy an object, I would then have to go through all my data structures looking for references to it to clean up. That sounds a lot like a garbage collector to me and I have to wonder whether the performance benefit of reducing object references would be cancelled out by my own ‘DIY’ garbage collector.
One thing that made Approach 1 easier for me was to implement resource pooling. The idea is that you allocate an arbitrary number of objects up-front and then use a resource pool class to maintain the collection. When you need a new object, rather than allocate and risk garbage collection, you simply get a ‘ready-rolled’ one from the pool.
Luckily for me, Thomas H. Aylesworth has written a pretty cool generic class to allow you to pool any type of object. The only constraint is that the object must have a parameterless constructor. The reason is that you will need to be able to get an ‘empty’ object out of this pool and fill it with your data before it can be used. If it didn’t have a parameterless constructor, the pool would have trouble allocating all the objects you need up front as it would not know what parameters were appropriate for the constructor – it is a generic class after all.
I’ve added the class to my own engine, and you can too provided you leave his copyright and license information intact as I have done.
Using the pool class
modelPool = new Pool(10); effectPool = new Pool (5); instancePool = new Pool (100);
This code shows how you would initialise the pool. Note, the Effect class in XNA does not have a parameterless constructor so cannot be used directly with the Pool class. I simply created my own Effect class and wrapped the XNA Effect class up in it. To ensure my code then refers to my Effect class rather than the XNA one, I add the following uses directive to the top of each source code file:
using Effect = XNAEngine.Graphics.Effect; using XnaEffect = Microsoft.Xna.Framework.Graphics.Effect;
Then I made sure my Effect class had a parameterless constructor. This was all made a lot simpler by the fact that I wanted to customise the Effect and Model classes myself anyway and while the XNA classes are useful, your own class can be less generic than theirs and ideally faster.
Pool.Node effectNode = effectPool.Get();
This doesn’t allocate any memory. The Get() method on the Pool object simply marks one of it’s unused pre-allocated objects as ‘in-use’ and returns a reference to it. To access the Effect object itself, simply use the .item property.
effectPool.Return(effectNode);
This code returns the node back to the Pool, ready for re-use later. It isn’t destroying the object though, it’s there waiting if we need it again!
Conclusion
As Shawn said, “you must pick just one path and follow it to the bitter end”, which I plan on doing. I don’t think I ever would have taken the Approach 2 path he describes, but there may come a time when I need it so it’s nice to know the option exists.

Trackback this post | Subscribe to the comments via RSS Feed