Thoughts on the GC

The Longer You Wait…

Despite having a decent collection rate for my GC, I was still having an issue with the latency every time it collected; it was long enough to cause a pretty annoying skip–for an interesting read on measuring the GC, check out Shawn Hargreaves: How to tell if your Xbox garbage collection is too slow

The best solution would to be to go after the source and optimize the areas that are causing the excess garbage; however, I just wanted a quick fix for the moment so I could continue working on my gameplay.

So, to get around the issue, I set my GC to collect every 250 KBs of collected garbage. I still get a stutter and it obviously occurs four times as much than before, but it’s very negligible; it’s to the point where if I were noticing only in an online match, I wouldn’t mind.

That being said, the GC still something I’m going to have to deal with, but at this point, I know that it won’t be an issue for me. I haven’t implemented yet, but using this concept, I’ll be able to hide the stutter in such a way that it’ll actually be useful. My players are humanoid characters and since I use skinning they have feet that walk. I’ll just set the GC to collect every step taken, and “blend” the stutter to give it the appearance. If the players not walking, I’m sure I can blend it else where, but knowing that you can cut the GC short successfully is very useful.

Be Mindful

While I’m working on a project, I always have a GC counter displayed on screen; it’s nothing special, I just use the GC.GetTotalMemory(false) method, convert it to a string, and pass it into my HUD for drawing.

The problem that I’ve ignored for far too long was my GC’s allocation and collection rate; it’s been so sporadic that the numbers on my screen have been nothing but a blur. I wasn’t all that familiar with GC, just the typical Value Type vs. Reference Type concept, but even there I’ve just skimmed the surface in understanding it.

To take on the GC, I didn’t use a profiler, rather I monitored my GC for hot spots. I commented out as much code as I could until my GC reached a pretty stable rate. Then, piece by piece, I reinstated parts of my code noting any changes to the GC. If the rate increased enough, I considered it a hot spot, and thus began work on alleviating any causes.

Right off the bat, I came across an issue that was really just an error on my part. For my collision detection, I have a map class which contains an array of bounds; to test an object against them, I have to iterate through the array. Seems fine and dandy, except rather than accessing my bounds locally, I was using my Map’s public accessor. Something like:

public Bound[] Bounds { get { return aryBounds; } }

I should mention that I was using this each and every time I wanted to access one of my bounds–oops. Ignoring the fact that my Bound Object is a class of its own, the returning of an array was (creating garbage x number of times accessed x number of active units). It’s no wonder my numbers were a blur. A simple fix; I changed my accessing from Bounds[i] to aryBounds[i].

This did alleviate quite a bit, but it still wasn’t enough. My Bounds also had some generic properties to help lump things together, but this too was causing some problems with garbage.

The following is the general layout of my Bounds:


public interface ICollidable
{

    BoundType BoundType { get; }
    Object CollisionBound { get; }

}

public abstract class Bound : ICollidable
{

    public Bound() { }
    public abstract Object CollisionBound { get; }

}

public class BoundBox : Bound
{

    private BoundingBox bound;
    public override BoundType BoundType { get { return BoundType.Box; } }
    public override Object CollisionBound { get { return bound; } }

}

public class BoundSphere : Bound
{

    private BoundingSphere bounds;
    public override BoundType BoundType { get { return BoundType.Sphere; } }
    public override Object CollisionBound { get { return bounds; } }

}

In order to accommodate my ICollidable interface, I use the standard Object type as a return value and use the is operator to cast it into its appropriate form:


if (aryBounds[i] is BoundBox)
{

    (BoundingBox)((BoundBox)aryBounds[i]).CollisionBound

}

I originally thought that my problem was using the is operator, but I was wrong. That actually didn’t seem to affect the GC at all. The problem was using the standard Object type; the cast from Object to BoundingBox or BoundingSphere was creating garbage. Since this was occuring for each given bound, on top of each active unit, this was creating lots and lots of garbage–it was a hell of a hot spot.

To solve the issue, I added a specific accessor for each of my Bound types. BoundBox got an accessor that returns a BoundingBox and likewise for BoundSphere. I still use the is operator to branch into the appropriate BoundType, but once I’m there, I no longer use my generic CollisionBound which returned an Object, but rather its accessor that returns its specific bounding structure.

This alone had my collection rate go from 35 seconds to 2 minutes and 53 seconds! I’m not kidding; I think watching paint dry might be more exciting than having watched my GC the second time around.

Another problem I was having was how I was resolving my collisions. To process a collision, I would pass my unit’s bound into my map’s Collision method which would then return a structure called CollisionData:


public struct CollisionData
{

    public bool Collision;
    public List WorldSurfaces;
    public List<Nullable> HitDistances;
    public List Elasticities;
    public List Frictions;

}

When my map determined a collision, it would add one value to each of the data’s lists. Afterwards, the CollisionData would be passed into my unit for processing. The problem was that the Lists were generating garbage. Even though CollisionData is a structure and the lists are of value types, passing around the full package was creating garbage–arrays and lists are a “special case” in regards to the garbage they create.

The solution was pretty simple, and it even worked out for the better in a few other places, but that’s besides the point. Rather than creating a new CollisionData each and every time, I gave each unit its own CollisionData, which I now pass into my map via ref aryUnits[i].CollisionData. No more passing around the lists or arrays; instead, my map works directly with my units’ CollisionData.

Lessons Learned?
The is operator is a good choice to use alongside the GC. Sometimes I would use the as operator to make the code appear more elegant:

BoundBox boxHold = aryBounds[i] as BoundBox;
if (boxHold != null) { // Do something }

“boxHold” creates garbage and can be avoided by using the is operator instead.

Unless you really really have to, avoid using the Object type directly. The extra casting that you’ll be required to perform will add garbage; also, if the particular operation is iterated over many times, you’ll add lots of garbage.

Take extra care when using Arrays and Lists, even if they’re of value types; they are special cases that will sometimes create garbage by passing the reference to it and not the object itself.

  1. Leave a comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: