Está en la página 1de 5

The Performance of Arrays

Chris Burrows 11 Jun 2010 2:01 PM 15

Stop me if you’ve heard this one, but here’s some information about how arrays perform, and a neat trick you can
get some performance back.

Some background
In .NET, arrays of reference types are covariant in their element type, but not safely. Eric, as always, has a post t
more deeply if you want to refresh your memory. The upshot is that if you have a Derived[], you can convert it to
that way. For instance,

class Base { }
class Derived : Base { }

class Program
{
static void Main()
{
Derived[] derivedArray = new Derived[10];
// This is the covariant conversion
Base[] baseArray = derivedArray;

for (int i = 0; i < baseArray.Length; ++i)


{
// Putting a Derived into our Base[] is ordinary polymorphism
baseArray[i] = new Derived();
}
}
}

Allowing this conversion from Derived[] to Base[] is not safe because now the compiler can’t help you know if you
when you set array elements. In the preceding example, I can put the Derived in the array, but imagine if I had a
derived class, OtherDerived. Putting those things in the array must fail, since the actual array can contain only De
OtherDeriveds are not Deriveds. In Eric’s perhaps more folksy example, “you can’t put a Turtle into an array of Gi

class Base { }
class Derived : Base { }
class OtherDerived : Base { }

class Program
{
static void Main()
{
Derived[] derivedArray = new Derived[10];
// This is the covariant conversion
Base[] baseArray = derivedArray;

for (int i = 0; i < baseArray.Length; ++i)


{
// Putting a OtherDerived into our Base[] is ordinary
// polymorphism. However, this is going to cause a runtime
// exception because the actual array won't be able to
// store an OtherDerived.
baseArray[i] = new OtherDerived();
// Unhandled Exception: System.ArrayTypeMismatchException:
// Attempted to access an element as a type incompatible
// with the array.
}
}
}

Well now to the point I want to address: where did that exception come from? It came from the runtime, which w
position to know what the real type of the array object was, and the real type of the element. It needed to determ
assignment was allowable and throw if not. Now if you think about this for a minute, you can see that it’s going to
that check for every assignment to an array element, right? The compiler just can’t know if any assignment to a c
ever going to work, and the runtime is always going to have to be there to manage the potential for failure. Can w
Does it make sense to? What does the check cost you?

A workaround
Well, if it’s doing that check for every assignment to an array, that’s probably going to cost you something. Let’s s
of the check to determine what the cost is. Remember I said that arrays are covariant only for reference types? W
that information. If we have an array of value types, the runtime isn’t going to have to perform that check anymo
that the IL the compiler emitted wasn’t going to allow assignment of anything but that value type to that array.

So if I want to have an array of reference types, but get the runtime to treat it like an array of value types, what
Just wrap the reference type in a value type. There you go. That’s the trick. Let’s see what that does.

I’m going to create a simple value type that merely holds a reference. I am even going to make it generic. And to
simple as I can, that’s all I’ll do. You could imagine a potentially easier-to-use version of this code that has implic
nice constructors, but let’s not do any of that.

struct Reference<T> where T : class


{
public T Value;
}

Ok, so, wow, what is that thing? It’s no bigger than the reference we started with. And if I want to make an array
get rid of covariance, I can. And here’s how I’d use it.

class Base { }
class Derived : Base { }

class Program
{
static void Main()
{
Reference<Base>[] baseArray = new Reference<Base>[10];

for (int i = 0; i < baseArray.Length; ++i)


{
baseArray[i] = new Reference<Base> { Value = new Derived() };
}
}
}

Now I have an array assignment that doesn’t need the runtime to perform a check anymore, and the reason is be
for there to be anything in “baseArray” except for an array of the exact type I said. The struct-ness of Reference<
possibility of anyone having performed a covariant conversion as existed in our previous examples. So I got rid of
But I added the initialization of a value type. Did I win or lose? Let’s do some timing to figure it out.

The experiment
Here’s my code. I’m going to time two different things: First, I’ll put a lot of Deriveds into a Base[], and then I’ll p
Reference<Base>s that hold Deriveds into a Reference<Base>. I’m using huge arrays just because I want to prov
jitter isn’t going to lift some of these operations out of the loop. And I’m producing Release binaries that will run o
framework on my 64-bit machine. I’ll time each example a few times to be sure. Here’s the code.

class Base { }
class Derived : Base { }

struct Reference<T> where T : class


{
public T Value;
}

class Program
{
const int Count = 1 << 27;

static void Main()


{
Stopwatch watch = new Stopwatch();

También podría gustarte