Use the MeasureIt program described in Chapter 1 to see for yourself the difference in iterating collections using for loops or foreach. Using standard for loops is significantly faster in all the cases. However, if you do your own simple test, you might notice equivalent performance depending on the scenario. In many cases, .NET will actually convert simple foreach statements into standard for loops.
Take a look at the ForEachVsFor sample project, which has this code:
int[] arr = new int[100]; for (int i = 0; i < arr.Length; i++) { arr[i] = i; } int sum = 0; foreach (int val in arr) { sum += val; } sum = 0; IEnumerable<int> arrEnum = arr; foreach (int val in arrEnum) { sum += val; }
Once you build this, then decompile it using an IL reflection tool. You will see that the first foreach is actually compiled as a for loop. The IL looks like this:
// loop start (head: IL_0034) IL_0024: ldloc.s CS$6$0000 IL_0026: ldloc.s CS$7$0001 IL_0028: ldelem.i4 IL_0029: stloc.3 IL_002a: ldloc.2 IL_002b: ldloc.3 IL_002c: add IL_002d: stloc.2 IL_002e: ldloc.s CS$7$0001 IL_0030: ldc.i4.1 IL_0031: add IL_0032: stloc.s CS$7$0001 IL_0034: ldloc.s CS$7$0001 IL_0036: ldloc.s CS$6$0000 IL_0038: ldlen IL_0039: conv.i4 IL_003a: blt.s IL_0024 // end loop
There are a lot of stores, loads, adds, and a branch—it is all quite simple. However, once we cast the array to an IEnumerable<int> and do the same thing, it gets a lot more expensive:
IL_0043: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1&lt;!0&gt; class [mscorlib]System.Collections.Generic.IEnumerable`1&lt;int32&gt;::GetEnumerator() IL_0048: stloc.s CS$5$0002 .try { IL_004a: br.s IL_005a // loop start (head: IL_005a) IL_004c: ldloc.s CS$5$0002 IL_004e: callvirt instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1&lt;int32&gt;::get_Current() IL_0053: stloc.s val IL_0055: ldloc.2 IL_0056: ldloc.s val IL_0058: add IL_0059: stloc.2 IL_005a: ldloc.s CS$5$0002 IL_005c: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext() IL_0061: brtrue.s IL_004c // end loop IL_0063: leave.s IL_0071 } // end .try finally { IL_0065: ldloc.s CS$5$0002 IL_0067: brfalse.s IL_0070 IL_0069: ldloc.s CS$5$0002 IL_006b: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_0070: endfinally } // end handler
We have 4 virtual method calls, a try-finally, and, not shown here, a memory allocation for the local enumerator variable which tracks the enumeration state. That is much more expensive than the simple for loop. It uses more CPU and more memory!
Remember, the underlying data structure is still an array—a for loop is possible—but we are obfuscating that by casting to an IEnumerable. The important lesson here is the one that was mentioned at the top of the chapter: In-depth performance optimization will often defy code abstractions. foreach is an abstraction of a loop, and IEnumerable is an abstraction of a collection. Combined, they dictate behavior that defies the simple optimizations of a for loop over an array.
Source :
For all your application development needs, visit www.verbat.com for a fiscally conscious proposal that meets your needs ( So I can keep this blog going as well!!!!)
Alternatively click through the link if you found this article interesting. (This will help the companies Search engine rankings)
Leave a Reply