views:

361

answers:

2

I'm working on a game for the xbox360, using XNA. On the Xbox the garbage collector performs rather badly compared to the one on a PC, so keeping garbage generated to a minimum is vital for a smoothly performing game.

I remember reading once that calling a delegate creates garbage, but now for the life of me can't find any references to delegates creating garbage. Did I just make this up or are delegates messy?

If delegates are messy, bonus points for suggesting a workaround.

public delegate T GetValue<T>(T value, T[] args);

public static T Transaction<T>(GetValue<T> calculate, ref T value, params T[] args) where T : class
{
    T newValue = calculate(value, args);
    return foo(newValue);
}

My code looks vaguely like that at the moment, the only solution I can think of to rid myself of delegates is to pass in a class which inherits an interface IValueCalculator, and then I can call the method on that interface, that's not really very neat though!

+6  A: 

A delegate is itself an object, so if you create a delegate, perhaps for an anonymous method, and give this to some other method to execute, and don't store the delegate for future reference, then yes, that will produce garbage.

For instance, this:

collection.ForEach(delegate(T item)
{
    // do something with item
});

In this case, a new delegate object is created, but beyond the call to ForEach it is not referenced, and thus eligible for garbage collection.

However, calling delegates does by itself not produce garbage, any more so than calling any other method of the same type would. For instance, if you call a delegate that takes an Object parameter, passing in an Int32 value, this value will be boxed, but that would happen if you called a normal method the same way as well.

So using delegates should be fine, but excessive creation of delegate objects will be a problem.


Edit: A good article on memory management for Xbox and XNA is here: Managed Code Performance on Xbox 360 for XNA: Part 2 - GC and Tools. Pay attention to this quote:

So how does one control GC latency? Like NetCF for devices, the Xbox GC is non-generational. That means every collection is a full collection on the managed heap. Thus, we find that GC latency is approximately linear to the number of live objects… then add the cost of heap compaction on to that. Our benchmarks show that the difference between deep object hierarchies vs. shallow ones is negligible, so it’s mostly the number of objects that matter. Small objects also tend to be a somewhat cheaper to deal with than big objects.

As you can see, try to avoid creating lots of unnecessary objects, and you should fare better.

Lasse V. Karlsen
I see, well that's good to know, although trying to use delegates without creating any (except at load time) is going to be interesting.
Martin
Just about anything in C# creates garbage this way. I wouldn't advise to avoid the use of short-lived references.
Henk Holterman
Normally I wouldn't either, but on the Xbox, with the XNA platform, if you can postpone GC (ie. make it run less often) without adverse effects, that's really a good idea, since it *will* make for choppy gameplay if it occurs too often. So being aware of what contributes to GC, and when it does it, is a good idea if you want to fix it. For instance, in the code I showed, if you're running that ForEach loop many times, often, perhaps you should store the delegate in a variable somewhere, if that doesn't change the behaviour. Game optimizations are usually different from normal desktop DB-apps.
Lasse V. Karlsen
+10  A: 

You're not thinking about this the right way. Garbage is effectively free. What you want to worry about is how much non-garbage you are producing. Remember how the garbage collector works: it first marks all known objects, then it clears the mark on all live objects and compacts the live objects. The expensive step there is "unmark the live objects". Destroying the garbage is cheap; it's identifying the live objects that is expensive, and that cost depends on the number of live objects you have (and the complexity of their reference topology), not on the number of dead objects you have. Don't worry about creating garbage, worry about creating live working set.

Regardless, if you have a problem with memory performance then the right thing to do is to get out the memory profiler and use it to analyze your program. Casting about at random wondering if this or that happens to allocate memory is like trying to weed your garden with nail scissors; it takes a lot of time and doesn't actually accomplish your goals. Use a profiler to analyze your performance and see where the problems are, and then fix them.

Eric Lippert
I tend to agree with you, and I don't really worry about garbage a lot - I just like to be informed!However, are you certain this is the case on the xbox, where the collector is non generational?
Martin
It's still mark-n-sweep even if its not generational.
Eric Lippert
The performance of the Xbox XNA GC is relative to the number of live/dead objects, so yes, try to keep the total number of objects down. And yes, use a memory profiler to figure out if you really have a problem.
Lasse V. Karlsen