Do you hate garbage? I do, I hate it a lot. There’s nothing more dissatisfying than playing your game and then HITCH, the garbage collector kicks in and takes a big-juicy bite from your framerate. I recently got through doing a major refactoring to my game engine (now it’s actually suited for some platformer action) and became interested in how much garbage was being generated. To find some of the basic stuff causing garbage I created a test scenario that told my game engine to render a lot of stuff at once. After some scrutiny with the XNA Framework Remote Performance Monitor I was able to find (and document) a majority of the garbage-generating sections of my engine. The results were terrifying. 7 Garbage Collections lasting roughly 80 milliseconds were occurring every frame. In other words, a majority of my CPU time was spent simply cleaning up after my engine! Not good. So I got on my classic programmer mind set and began commenting things out until I could hone down into the source of garbage. After I got most of the core code cleaned up I ran my work-in-progress game (Nut Harvest 360) to what else I could find out. A lot of things I discovered surprised me.
Iterating though a Current Technique Passes using foreach generates garbage:
This was a tough one. I had nearly all my code commented out that I thought would cause garbage, but yet the Perf. Monitor told me that boxing was happening somewhere.
So I continued searching (ludicrously commenting out additional statements), until I was left with my Primitive Rendering Class. All it was doing was iterating over an EffectPass collection like so:
foreach (EffectPass pass in PBBasicShader.CurrentTechnique.Passes)
{
Pass.Begin();
Game.GraphicsDevice.VertexDeclaration = this.vertices;
Game.GraphicsDevice.DrawUserPrimitives(type, this.elements, 0, this.NumOfPrimitivesToRender);
Pass.End()
}
Pretty normal eh? So after scratching my head for a second I disabled the body of the code. But there was still boxing! I was getting discouraged at this point. Surely it couldn’t be the foreach could it? A moment of doubt passed. I commented out the foreach and… the boxing problem went away! I never thought that iterating over an EffectPass collection would generate garbage, (I mean, several XNA books I have use this technique) but tests confirmed that it does. After rewriting the above as:
for (int i = 0; i < technique.Passes.Count; ++i)
{
PBBasicShader.CurrentTechnique.Passes[i].Begin();
Game.GraphicsDevice.VertexDeclaration = this.vertices;
Game.GraphicsDevice.DrawUserPrimitives(type, this.elements, 0, this.NumOfPrimitivesToRender);
PBBasicShader.CurrentTechnique.Passes[i].End();
}
The boxing/garbage problem when away! I found the same problem in my ParticleSystem. After I swithced to a regular for instead of a foreach I no longer received boxed value types.
Be very careful when it comes to boxing. (If you don’t know what boxing is take a look here.) Some of the most simple methods that you think don’t box actually do. I dug into my Camera class and found that the statements Vector2.Equals(val, val2) was generating garbage. How could a comparision between two Vector2’s result in garbage?, I thought. Well, I looked at the Equals definition and found that it actually takes a reference of two Objects. The Vector2’s were being boxed into an Object, and then compared, when resulted in garbage, and thus garbage collections. I changed this to the val.Equals(val2) method and removed the boxing problems.
Anytime you use the new keyword you are generating garbage (unless it’s a value type such as an int, struct, etc.) This was my largest problem next to boxing. I had too many methods that were dynmanically creating arrays every time they were called (often every frame.) This was remidied by removing the array from the method and instead have the method accept an array by ref. This puts a bit more work into the calling code (as you specifically have to set up an array in every class that uses the method) but it’s worth it in terms of how much garbage you are saving yourself.
In my InputManager I found that having a list of enumeration types causes boxing each time a value is added.
The problem code was these two lines:
List
connectedControllers = new List
(MAX_CONTROLLERS);
connectedControllers.Add(PlayerIndex.One);
I don’t really understand why…perhaps the list does some internal operations? I read on Shawn’s blog that using an enum as a key in a Dictionary causes boxing due to internal operations, so perhaps this is something similar. This was remidied by making the List accept ints and casting the PlayerIndex value to an int before storing it into the array.
Solution:
List connectedControllers = new List(MAX_CONTROLLERS);
connectedControllers.Add((int)PlayerIndex.One);
Be very wary of using Linq extensions, as a majority of them box values in a heartbeat. If its possible to make your own method, than it may be best to do so. I was making some heavy use of the Linq function Except (returns an array of all the data in the first set not present in the second set.) I was able to reduce garbage and boxing by creating my own function.
Problem:
IEnumerable
set3 = set1.Except
(set2);
Solution:
private void SetDifference(List < T > set1 , List < T > set2, ref List setDifference)
{
if (setDifference == null)
return;
setDifference.Clear();
foreach (T obj in set1)
if (!set2.Contains(obj))
setDifference.Add(obj);
}
SetDifference
(set1, set2, ref set3);
Here is one that broke my heart: Using the List sort functionality produces garbage! This was (and still is) a baddie for me, as I sort all of my objects each frame multiple times before drawing them (once for their Update Order and once for their Draw Order.) The only way to get around this is to either use an array or to create your own sort method. (Please note that casting your List to an Array to Sort is a very very bad idea, as calling ToArray() or ToList() creates a new array and then returns it, resulting in garbage.
Well there ya go, a few general performance tidbits to keep in mind. I hope they prove useful.