views:

162

answers:

2

I'm working on a piece of code in C# / XNA where I'm highly concerned with performance. Part of this is passing several structs that are stored in arrays to various functions.

Before this is asked, these are indeed supposed to be structs, not classes. They're essentially value types, and they need to (basically) live on the stack. There are many of them, they come and go very quickly, and getting the garbage collector involved for them (even if I were running pools) would be expensive.

I've already improved performance quite a bit by passing by reference, but I'm wondering what the performance implications of that is when the same struct at the same index of an array is passed to several different functions by reference. I assume that in order for this to all work, C# has to internally pin the array pointer before passing the struct. Would I gain performance by pinning the struct first and passing the pointer instead?

For example. If I have something like:

for(int i = 0; i < array.Length; ++i)
{
    value = Function1(ref array[i]);

    // Lots of code....

    otherValue = Function2(ref array[i]);

    // Lots of code....

    anotherValue = Function3(ref array[i]);
}

Doesn't C# essentially have to do this?

for(int i = 0; i < array.Length; ++i)
{
    pin(array);
    value = Function1(ref array[i]);
    unpin(array);

    // Lots of code....

    pin(array);
    otherValue = Function2(ref array[i]);
    unpin(array);

    // Lots of code....

    pin(array);
    anotherValue = Function3(ref array[i]);
    unpin(array);
}

And would I be better off doing this?

for(int i = 0; i < array.Length; ++i)
{
    fixed(struct* working = ^array[i]) 
    {
        value = Function1(working);

        // Lots of code....

        otherValue = Function2(working);

        // Lots of code....

        anotherValue = Function3(working);
    }
}

Or, even better,

fixed(struct* working = ^array[0]) 
{
    for(int i = 0; i < array.Length; ++i)
    {
        value = Function1(working[i]);

        // Lots of code....

        otherValue = Function2(working[i]);

        // Lots of code....

        anotherValue = Function3(working[i]);
    }
}

Or is the C# compiler / JITter smart enough to automatically pin the array?

+10  A: 

You're confusing managed references with pointers.

A managed reference never needs to be pinned, even if it points to an element in an array, because the GC "knows about" the reference and will update it if the array is moved.

Pinning is only necessary for unmanaged pointers in unsafe code, and should be avoided where possible for performance reasons.

SLaks
+1 To drive home the point, using `fixed` in this case would negatively impact performance, possibly significantly.
280Z28
That's what I needed to know. Thanks!
Jeff
A: 

I'm not sure about XNA (it's a different CLR), but .NET Framework (as of 3.5) doesn't pin things passed by reference (by default unless they're being passed to unmanaged code. If the GC is invoked, it can still move the data, since the stack is scanned for references. If there's unmanaged code going on at some level, then, yes, it does need to be pinned.

Robert Fraser