In the following scenario, is example 1 faster than example 2? Why?
Example 1
int c = myArray.Count;
for (int i = 0; i < c; i++)
{
Console.WriteLine(myArray[i]);
}
Example 2
for (int i = 0; i < myArray.Count; i++)
{
Console.WriteLine(myArray[i]);
}
Lets take the IL code and see what is going on in Release configuration.
/* 0x0000027B 6F0D00000A */ IL_001F: callvirt instance int32 class [System.Collections]System.Collections.Generic.List`1::get_Count() /* 0x00000280 0B */ IL_0024: stloc.1 /* 0x00000281 16 */ IL_0025: ldc.i4.0 /* 0x00000282 0C */ IL_0026: stloc.2 /* 0x00000283 2B10 */ IL_0027: br.s IL_0039 // loop start (head: IL_0039) /* 0x00000285 06 */ IL_0029: ldloc.0 /* 0x00000286 08 */ IL_002A: ldloc.2 /* 0x00000287 6F0E00000A */ IL_002B: callvirt instance !0 class [System.Collections]System.Collections.Generic.List`1::get_Item(int32) /* 0x0000028C 280F00000A */ IL_0030: call void [System.Console]System.Console::WriteLine(char) /* 0x00000291 08 */ IL_0035: ldloc.2 /* 0x00000292 17 */ IL_0036: ldc.i4.1 /* 0x00000293 58 */ IL_0037: add /* 0x00000294 0C */ IL_0038: stloc.2 /* 0x00000295 08 */ IL_0039: ldloc.2 /* 0x00000296 07 */ IL_003A: ldloc.1 /* 0x00000297 32EC */ IL_003B: blt.s IL_0029 // end loop /* 0x00000299 16 */ IL_003D: ldc.i4.0 /* 0x0000029A 0D */ IL_003E: stloc.3 /* 0x0000029B 2B10 */ IL_003F: br.s IL_0051 // loop start (head: IL_0051) /* 0x0000029D 06 */ IL_0041: ldloc.0 /* 0x0000029E 09 */ IL_0042: ldloc.3 /* 0x0000029F 6F0E00000A */ IL_0043: callvirt instance !0 class [System.Collections]System.Collections.Generic.List`1::get_Item(int32) /* 0x000002A4 280F00000A */ IL_0048: call void [System.Console]System.Console::WriteLine(char) /* 0x000002A9 09 */ IL_004D: ldloc.3 /* 0x000002AA 17 */ IL_004E: ldc.i4.1 /* 0x000002AB 58 */ IL_004F: add /* 0x000002AC 0D */ IL_0050: stloc.3 /* 0x000002AD 09 */ IL_0051: ldloc.3 /* 0x000002AE 06 */ IL_0052: ldloc.0 /* 0x000002AF 6F0D00000A */ IL_0053: callvirt instance int32 class [System.Collections]System.Collections.Generic.List`1::get_Count() /* 0x000002B4 32E7 */ IL_0058: blt.s IL_0041 // end loop
There is one obvious difference between both, in the latter method you are calling virtual instance method on each iteration and on the other only once before the loop.
IL instructions are relatively the same number so unless you think that this callvirt
(why callvirt for instance method? because it has nice null check that comes with it and compiler uses it for non-virtual methods as well) instruction will drag you down I am suggesting you do choose what is best practice that potentially small performance tweak will not worth it I guarantee it, not to mention that JIT can also do some optimizations - I will be not surprised.
UPDATE: Benchmarks with BenchmarkDotNet with attaached debugger.
Method | Mean | Error | StdDev |
------------- |---------:|----------:|----------:|
OutsideCount | 25.04 ns | 0.3334 ns | 0.2955 ns |
InsideCount | 26.13 ns | 0.5295 ns | 0.6502 ns |
Foreach | 40.59 ns | 0.3848 ns | 0.3599 ns |
Again this is very hardware specific, but showing it for the sake of argument.