Question

I'm trying to take advantage of iterators in C# to clean up some spatial queries on objects in a game I'm making.

Here's what I'm doing currently:

    public struct ObjectInfo
    {
        public int x, y;
        public int Type;
        public int hp;
    }

    public static IEnumerable<ObjectInfo> NearbyObjects(int x, int y, int distance)
    {
        // Not actually what I'm doing, but for simplicity...
        for (int i = 0; i < 10; ++ i)
        {
            yield return new ObjectInfo();
        }
    }

    public static void Explode()
    {
        foreach (ObjectInfo o in NearbyObjects(0, 0, 1))
        {
            o.hp = 0;
        }
    }

This works great except I think there is some boxing/unboxing going on. The CLRProfiler seems to verify this as I'm seeing allocations happening in my Explode method. I'd very much like to avoid any allocations after startup so I'm not triggering the garbage collector in the middle of a level or something. Is there some way I can keep this syntax while avoiding any allocation? Maybe by implementing my own iterators or something?

Was it helpful?

Solution

The allocations in your Explode method are due to the internal workings of the iterator itself, not due to boxing.

Every iterator block is implemented by the compiler using a reference class to maintain the state which is used in the course of iterating over the IEnumerable, in your case with foreach. Normally the allocation of this class is not noticed as a problem because the iterator iterates over a large collection or the client who is iterating is doing substantial work of their own or allocation that overwhelm the iterators allocation.

But if your collection is small (as yours is), and the foreach is very fast and has no allocations of its own, all that will be left is the allocation of the iterator state class.

To solve this performance problem you can simply reorganize your code to use for instead of foreach whereever the profiler tells you a lot of memory is being allocated in an iterator block. It's usually a modest change and the performance boost if measurable is then worth it.

OTHER TIPS

Your code does not do any boxing.

Boxing only happens when you put a struct into an object field.
Since your code is fully strongly-typed and doesn't have any object fields, it doesn't box.

Calling an iterator method will create a new iterator object (which implements both IEnumerable<T> and IEnumerator<T>), but should not result in any other (behind-the-scenes) allocations.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top