Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are the performance implications of passing structs in an array by reference in C#

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?

like image 440
Jeff Avatar asked Dec 22 '22 07:12

Jeff


1 Answers

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.

like image 145
SLaks Avatar answered Dec 30 '22 10:12

SLaks