Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When to use an array of a value type containing a reference types over an array of a reference type?

Suppose I have the following:

public class MyElement
{
}

[Serializable]
[StructLayout(LayoutKind.Sequential)]
struct ArrayElement
{
    internal MyElement Element;
}

public class MyClass
{
    internal MyElement ComputeElement(int index)
    {
        // This method does a lengthy computation.
        // The actual return value is not so simple.
        return new MyElement();
    }

    internal MyElement GetChild(ref MyElement element, int index)
    {
        if (element != null)
        {
            return element;
        }

        var elem = ComputeElement(index);
        if (Interlocked.CompareExchange(ref element, elem, null) != null)
        {
            elem = element;
        }

        return elem;
    }
}

public class MyClassA : MyClass
{
    readonly MyElement[] children = new MyElement[10];

    public MyElement GetChild(int index)
    {
        return GetChild(ref children[index], index);
    }
}

public class MyClassB : MyClass
{
    readonly ArrayElement[] children = new ArrayElement[10];

    public MyElement GetChild(int index)
    {
        return GetChild(ref children[index].Element, index);
    }
}

In what situation(s) would there be an advantage to using MyClassB over MyClassA?

like image 438
cubetwo1729 Avatar asked Sep 14 '13 14:09

cubetwo1729


2 Answers

ArrayElement is a wrapper that allows the JIT to generate better code. .NET arrays have runtime type checks for reference stores because they are not statically type safe in all regards.

var array = new Stream[10];
((object[])array)[0] = "somestring"; //runtime exception

With a wrapper the type check is no longer needed.

like image 29
usr Avatar answered Sep 18 '22 13:09

usr


To clarify usr's correct but somewhat sparse answer:

C# supports a feature -- my candidate for "worst feature in C#" -- called array type covariance. That is, if you have an array of turtles, you can assign it to a variable of type "array of animals":

class Animal {}
class Turtle : Animal {}
...
Animal[] animals = new Turtle[10];

This is "covariance" because the assignment compatibility rule for arrays is an arrow in the same direction as the assignment compatibility rule for its elements:

Turtle --> Animal
Turtle[] --> Animal[]

This is feature is not type safe because, well...

animals[0] = new Giraffe();

And we just put a giraffe into an array that is actually an array of turtles. The compiler cannot determine that type safety is violated here -- a giraffe is an animal -- so the check has to be performed by the runtime.

To prevent this from happening at runtime, the runtime inserts a check every time you put a Giraffe into an array of Animals to check if it is really an array of Turtles. Which it almost never is. But this check takes time, and so the feature effectively slows down every successful array access.

Unsafe array covariance only applies to arrays whose element types are reference types. It does not apply to value types. (This is a small lie; the CLR will allow you to cast int[] to object and then object to uint[]. But in general, covariance does not apply to value types.)

Therefore you can save on the expense of the check by making your array actually an array of value type, where the value type is just a wrapper for the reference. The size of the array will be unaffected, but the access to it will be slightly faster.

You should not pull these sorts of crazy tricks unless you have empirical evidence that doing so actually solves a practical performance problem. The number of situations in which this optimization is warranted is quite small, but there are a few places where this sort of thing can make a difference.

I note that you can also avoid the cost of the check by sealing the Turtle type and then using an array of Turtles. The runtime will reason that the array type cannot really be more derived because then its element type would derive from a sealed type, which is impossible.

like image 190
Eric Lippert Avatar answered Sep 19 '22 13:09

Eric Lippert