Coding for Performance : for vs. foreach


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&amp;lt;!0&amp;gt; class [mscorlib]System.Collections.Generic.IEnumerable`1&amp;lt;int32&amp;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&amp;lt;int32&amp;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 :  : Writing High-Performance .NET Code

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)

 

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Website Powered by WordPress.com.

Up ↑

%d bloggers like this: