Ozzah
Ozzah

Reputation: 10701

In C#, Is it slower to reference an array variable?

I've got an array of integers, and I'm looping through them:

for (int i = 0; i < data.Length; i++)
{
  // do a lot of stuff here using data[i]
}

If I do:

for (int i = 0; i < data.Length; i++)
{
  int value = data[i];
  // do a lot of stuff with value instead of data[i]
}

Is there any performance gain/loss?

From my understanding, C/C++ array elements are accessed directly, i.e. an n-element array of integers has a contiguous memory block of length n * sizeof(int), and the program access element i by doing something like *data[i] = *data[0] + (i * sizeof(int)). (Please excuse my abuse of notation, but you get what I mean.)

So this means C/C++ should have no performance gain/loss for referencing array variables.

What about C#? C# has a bunch of extra overhead like data.Length, data.IsSynchronized, data.GetLowerBound(), data.GetEnumerator().

Clearly, a C# array is not the same as a C/C++ array.

So what's the verdict? Should I store int value = data[i] and work with value, or is there no performance impact?

Upvotes: 25

Views: 9469

Answers (5)

Hans Passant
Hans Passant

Reputation: 942177

You can have the cake and eat it too. There are many cases where the jitter optimizer can easily determine that an array indexing access is safe and doesn't need to be checked. Any for-loop like you got in your question is one such case, the jitter knows the range of the index variable. And knows that checking it again is pointless.

The only way you can see that is from the generated machine code. I'll give an annotated example:

    static void Main(string[] args) {
        int[] array = new int[] { 0, 1, 2, 3 };
        for (int ix = 0; ix < array.Length; ++ix) {
            int value = array[ix];
            Console.WriteLine(value);
        }
    }

Starting at the for loop, ebx has the pointer to the array:

            for (int ix = 0; ix < array.Length; ++ix) {
00000037  xor         esi,esi                       ; ix = 0
00000039  cmp         dword ptr [ebx+4],0           ; array.Length < 0 ?
0000003d  jle         0000005A                      ; skip everything
                int value = array[ix];
0000003f  mov         edi,dword ptr [ebx+esi*4+8]   ; NO BOUNDS CHECK !!!
                Console.WriteLine(value);
00000043  call        6DD5BE38                      ; Console.Out
00000048  mov         ecx,eax                       ; arg = Out
0000004a  mov         edx,edi                       ; arg = value
0000004c  mov         eax,dword ptr [ecx]           ; call WriteLine()
0000004e  call        dword ptr [eax+000000BCh] 
            for (int ix = 0; ix < array.Length; ++ix) {
00000054  inc         esi                           ; ++ix
00000055  cmp         dword ptr [ebx+4],esi         ; array.Length > ix ?
00000058  jg          0000003F                      ; loop

The array indexing happens at address 00003f, ebx has the array pointer, esi is the index, 8 is the offset of the array elements in the object. Note how the esi value is not checked again against the array bounds. This runs just as fast as the code generated by a C compiler.

Upvotes: 24

Eric
Eric

Reputation: 51

Not really sure, but it probably wouldn't hurt to store the value if you are going to use it multiple times. You could also use a foreach statement :)

Upvotes: 0

Anthony Pegram
Anthony Pegram

Reputation: 126942

You have written it both ways. Run it both ways, measure it. Then you'll know.

But I think you would prefer working with the copy rather than always working with the array element directly, simply because it's easier to write the code that way, particularly if you have lots of operations involving that particular value.

Upvotes: 3

Ben Voigt
Ben Voigt

Reputation: 283793

The compiler can only perform common subexpression optimization here if it can prove that the array isn't accessed by other threads or any methods (including delegates) called inside the loop, it might be better to create the local copy yourself.

But readability should be your main concern, unless this loop executes a huge number of times.

All of this is also true in C and C++ -- indexing into an array will be slower than accessing a local variable.

As a side note, your suggested optimization is no good: value is a keyword, choose a different variable name.

Upvotes: 1

user541686
user541686

Reputation: 210745

Yes, there is a performance loss due to the bounds check for every access to the array.

No, you most likely don't need to worry about it.

Yes, you can should store the value and work with the value. No, this isn't because of the performance issue, but rather because it makes the code more readable (IMHO).


By the way, the JIT compiler might optimize out redundant checks, so it doesn't mean you'll actually get a check on every call. Either way, it's probably not worth your time to worry about it; just use it, and if it turns out to be a bottleneck you can always go back and use unsafe blocks.

Upvotes: 17

Related Questions