I’ve nearly completed my first major optimization pass: removing runtime memory allocations. I’ve removed string allocations, then I found all the sources of boxing garbage and removed them too. Here are some examples, they might benefit others.
I’ve made custom generic containers to handle game entities. I was iterating over them using the nice foreach construct. That’s cool, but, you guessed it, it was generating garbage. Why? Because I was implementing IEnumerable<T>.
public class AutoList<T> : IEnumerable<T>
private List<T> m_List;
public IEnumerator<T> GetEnumerator()
The problem here is returning a IEnumerator<T>. This causes boxing (conversion to an interface). Disassembly in Reflector make this obvious. So what’s the solution?
I haven’t found a perfect solution, but simply removing the IEnumerator<T> implementation and returning List<T>.Enumerator for GetEnumerator still allows foreach and is garbage free.
public class AutoList<T>
private List<T> m_List;
public List<T>.Enumerator GetEnumerator()
I’ve found other sources of garbage, and dealt with them the fastest way. In the Primitives3D sample:
foreach (EffectPass effectPass in effect.CurrentTechnique.Passes)
int passesCount = effect.CurrentTechnique.Passes.Count;
for (int pass = 0; pass != passesCount; pass++)
EffectPass effectPass = effect.CurrentTechnique.Passes[pass];
Maybe not so elegant, but efficient.
Using Nick Gravelyn’s excellent advice (I’d suggest reading his blog here, see forum thread), I’ve improved the garbage removal from my input wrapper (Dictionary) by implementing a IEqualityComparer class for my enum type. That enables still using enum without casting to ints, and also without boxing. It’s a bit tedious to write such trivial classes, but there are good .NET reasons below, the enum type providing nice facilities for that price.
Finally, I’ve started removing the sources of garbage collection really caused by my code. By really I mean: where I’m using new on reference types, really allocating memory on the garbage collector’s heap. And so I’ve created two generic Pool classes, one which is enumerable (as shown above of course), and the other not (pure pool). I can detail them if people are interested, for now I’d just paste the header, which contain an interesting use of C#’s generics, to ensure type is usable by Pool class:
public class BasicPool where T: class, new()
There is a lot of A-Life in NSN, so there were lots of allocations, the three more common being AI ships, missions and rumors. The ships are now using a pure pool, the missions and rumors shall use the enumerable version.
And that’s all for this very technical post. I will later write more general stuff, to avoid boring non-programmers :)