Click here to Skip to main content
15,885,842 members
Articles / Programming Languages / Visual Basic
Article

FOREACH Vs. FOR (C#)

Rate me:
Please Sign up or sign in to vote.
3.50/5 (81 votes)
19 Apr 20044 min read 644.8K   55   75
Checking the performance between foreach and for loops in depth.

Introduction

In my experience, there are two kinds of programmers. Those who write something to get the work done and those who want to write good code. But, here we get a big question. What is good code? Good code comes from good programming practices. What are good programming practices? Actually, my aim here is not to talk about good programming practices (I’m planning to write something related to that in future!), rather to talk more about writing something which will be more effective. I'm only going to look more deeper of two loops which are commonly used nowadays, and their differences in the aspect of performance.

Background

Must be familiar with IL and assembly code. Also, better to have a good knowledge on how .NET framework works. Some knowledge of JIT is also needed to understand what is exactly happening.

Using the code

I’m going to take a very small piece of code for two popular looping statements for and foreach. We will look some code and will see what it does, more in detail about the functionality.

FOR

C#
int[] myInterger = new int[1];
int total = 0;
for(int i = 0; i < myInterger.Length; i++)
{
    total += myInterger[i];
}

foreach

C#
int[] myInterger = new int[1];
int total = 0;
foreach(int i in myInterger) 
{
    total += i;
}

Both codes will produce the same result. foreach is used on top of collections to traverse through while for can be used on anything for the same purpose. I’m not going to explain whatsoever about the code. Before going in more deeper, I think all of you are familiar with ILDasm which is used to generate IL code, and CorDbg tool which is normally used to generate JIT compiled code.

The IL code produced by C# compiler is optimized up to certain extend, while leaving some part to JIT. Anyway, this is not really what matters to us. So, when we talk about the optimization, two things we must consider. First is C# compiler and the second is JIT.

So, rather than looking more deep into IL code, we will see more about the code which is emitted by JIT. That is the code which will run on our machine. I’m now using AMD Athlon 1900+. The code highly depends on our hardware. Therefore, what you may get from your machine may differ from mine up to a certain extend. Anyway, the algorithms wont change that much.

In variable declaration, foreach has five variable declarations (three Int32 integers and two arrays of Int32) while for has only three (two Int32 integers and one Int32 array). When it goes to loop through, foreach copies the current array to a new one for the operation. While for doesn't care of that part.

Here, I’m going into the exact difference between the codes.

FOR

Instruction                           Effect
ASM
cmp     dword ptr [eax+4],0           i<myInterger.Length
jle     0000000F
mov     ecx,dword ptr [eax+edx*4+8]   total += myInterger[i]
inc     edx                           ++i
cmp     esi,dword ptr [eax+4]         i<myInterger.Length
jl      FFFFFFF8

I’ll explain what is happening here. The esi register which holds the value of i and the length of myInteger array are compared at two stages. The first one is done only once to check the condition and if the loop can continue, the value is added. For the loop, it is done at the second stage. Inside the loop, it is well optimized and as explained, the work is done with perfect optimization.

foreach

Instruction                            Effect
ASM
cmp     esi,dword ptr [ebx+4]          i<myInterger.Length
jl      FFFFFFE3
cmp     esi,dword ptr [ebx+4]          i<myInterger.Length 
jb      00000009
mov     eax,dword ptr [ebx+esi*4+8] 
mov     dword ptr [ebp-0Ch],eax  
mov     eax,dword ptr [ebp-0Ch]
add     dword ptr [ebp-8],eax          total += i
inc     esi                            ++i
cmp     esi,dword ptr [ebx+4]          i<myInterger.Length
jl      FFFFFFE3

Anyone will say that both are not the same. But we will look why it differs from the for loop. The main reason for the difference is that both of them are differently understood by the compiler. The algorithm they are using is different. Two compare statements one after the other is unnecessary. It is doing the same thing again and again for no reason!

ASM
cmp                    esi,dword ptr [ebx+4]   
jl                         FFFFFFE3
cmp                    esi,dword ptr [ebx+4]

It also uses some unnecessary move statements which also may (not always, but depends) reduce the performance of the code. foreach is thinking everything as a collection and treating them as a collection. I feel, that will also reduce the performance of the work.

Therefore, I strongly feel if you are planning to write high performance code that is not for collections, use for loop. Even for collections, foreach may look handy when using, but it's not that efficient. Therefore, I strongly recommend everyone to use for loop rather than foreach at any stage.

Points of Interest

Actually, I did a small research on the performance issue of codes mainly on .NET languages. While I was testing, I found that it was really a must to know how JIT works and to debug the code generated by JIT compiler. It took some time to understand the code.

History

This is submitted on 19th of April 2004.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Singapore Singapore
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralRe: Have you considered.... Pin
LimitedAtonement1-Dec-09 5:29
LimitedAtonement1-Dec-09 5:29 
GeneralRe: Have you considered.... Pin
Colin Angus Mackay1-Dec-09 5:33
Colin Angus Mackay1-Dec-09 5:33 
GeneralRe: Have you considered.... Pin
Chester Ragel20-Apr-04 21:34
Chester Ragel20-Apr-04 21:34 
GeneralRe: Have you considered.... Pin
Colin Angus Mackay20-Apr-04 22:23
Colin Angus Mackay20-Apr-04 22:23 
GeneralRe: Have you considered.... Pin
James Curran23-Apr-04 12:02
James Curran23-Apr-04 12:02 
GeneralRe: Have you considered.... Pin
Colin Angus Mackay23-Apr-04 13:13
Colin Angus Mackay23-Apr-04 13:13 
GeneralRe: Have you considered.... Pin
James Curran23-Apr-04 18:20
James Curran23-Apr-04 18:20 
GeneralRe: Have you considered.... Pin
Colin Angus Mackay24-Apr-04 0:32
Colin Angus Mackay24-Apr-04 0:32 
Consider the following code. The size of the array is set at roughly the crossover point between the two different ways of iterating. If the array is smaller then the try/catch method will always come out slower, if the array is larger then the try/catch method wins.

The code was tested on a 650MHz Intel Pentium III running Windows XP Professional.

using System;
using System.Runtime;
using System.Runtime.InteropServices;

namespace IterationTest
{
    class Class1
    {

        [DllImport("kernel32.dll")]
        private static unsafe extern uint QueryPerformanceCounter(System.Int64* p);

        [DllImport("kernel32.dll")]
        private static unsafe extern uint QueryPerformanceFrequency(System.Int64* p);

        [STAThread]
        static unsafe void Main(string[] args)
        {
            byte[] bigArray = new Byte[1024*1024*5];

            System.Int64 frequency;
            System.Int64 startCount;
            System.Int64 endCount;
            System.Int64 counts;

            QueryPerformanceFrequency(&frequency);
            Console.WriteLine("Counter resolution = {0} Hz", frequency);

            // Perform the tests a number of times to get
            // a good average
            for(int n=0; n<10; n++)
            {
                Console.WriteLine("Starting run No. {0}", n);

                QueryPerformanceCounter(&startCount);
                for(int i=0; i<bigArray.Length; i++)
                    bigArray[i] = 30;
                QueryPerformanceCounter(&endCount);
                counts = endCount-startCount;
                Console.WriteLine("Test A result: {0} Ticks; {1} Seconds", counts, (double)counts/(double)frequency);


                QueryPerformanceCounter(&startCount);
                try
                {
                    for(int i=0;;i++)
                        bigArray[i] = 30;
                }
                catch
                {
                }
                QueryPerformanceCounter(&endCount);
                counts = endCount-startCount;
                Console.WriteLine("Test B result: {0} Ticks; {1} Seconds", counts, (double)counts/(double)frequency);
            }

            Console.ReadLine();
        }
    }
}


My results from this test are:
Counter resolution = 3579545 Hz
Starting run No. 0
Test A result: 319728 Ticks; 0.0893208494375682 Seconds
Test B result: 1090667 Ticks; 0.304694311707214 Seconds
Starting run No. 1
Test A result: 379242 Ticks; 0.105946984882157 Seconds
Test B result: 311542 Ticks; 0.0870339666074878 Seconds
Starting run No. 2
Test A result: 320939 Ticks; 0.0896591605916394 Seconds
Test B result: 307377 Ticks; 0.0858704109041792 Seconds
Starting run No. 3
Test A result: 322519 Ticks; 0.0901005574730867 Seconds
Test B result: 311134 Ticks; 0.0869199856406331 Seconds
Starting run No. 4
Test A result: 321386 Ticks; 0.0897840367979729 Seconds
Test B result: 306264 Ticks; 0.0855594775313622 Seconds
Starting run No. 5
Test A result: 322224 Ticks; 0.0900181447642089 Seconds
Test B result: 311791 Ticks; 0.087103528521083 Seconds
Starting run No. 6
Test A result: 320811 Ticks; 0.0896234018569399 Seconds
Test B result: 306223 Ticks; 0.0855480235616538 Seconds
Starting run No. 7
Test A result: 319364 Ticks; 0.0892191605357664 Seconds
Test B result: 306573 Ticks; 0.0856458013518478 Seconds
Starting run No. 8
Test A result: 323443 Ticks; 0.0903586908391988 Seconds
Test B result: 310447 Ticks; 0.086728061806738 Seconds
Starting run No. 9
Test A result: 323716 Ticks; 0.0904349575155502 Seconds
Test B result: 306733 Ticks; 0.0856904997702222 Seconds


Finally, I'd like to remind you what I said in another branch of this thread. 'By the way - in case anyone thinks that I am crazy enough have actually written a looping structure the way I described above the answer is "no I have not done this in a live environment".'. As far as I can see, the mechanism I described is interesting from an academic stand-point only. I can't forsee any situation where the size of an array (or number of iterations) would be consistently sufficiently large enough to make me consider the above structure as a serious method of looping.


"You can have everything in life you want if you will just help enough other people get what they want." --Zig Ziglar

Coming soon: The Second EuroCPian Event


GeneralRe: Have you considered.... Pin
adriancs22-Jan-13 20:59
mvaadriancs22-Jan-13 20:59 
GeneralHmmmm Pin
Stuart Dootson20-Apr-04 2:00
professionalStuart Dootson20-Apr-04 2:00 
GeneralRe: Hmmmm Pin
Chester Ragel20-Apr-04 2:30
Chester Ragel20-Apr-04 2:30 
GeneralRe: Hmmmm Pin
Dan Colasanti4-May-04 16:30
professionalDan Colasanti4-May-04 16:30 
GeneralBenchmarks Pin
Jonathan de Halleux19-Apr-04 21:32
Jonathan de Halleux19-Apr-04 21:32 
GeneralRe: Benchmarks Pin
NormDroid19-Apr-04 22:35
professionalNormDroid19-Apr-04 22:35 
GeneralRe: Benchmarks Pin
Jonathan de Halleux19-Apr-04 22:44
Jonathan de Halleux19-Apr-04 22:44 
GeneralRe: Benchmarks Pin
Jeff Martin20-Apr-04 3:37
Jeff Martin20-Apr-04 3:37 
GeneralRe: Benchmarks Pin
Jonathan de Halleux20-Apr-04 21:14
Jonathan de Halleux20-Apr-04 21:14 
GeneralRe: Benchmarks Pin
Colin Angus Mackay20-Apr-04 11:32
Colin Angus Mackay20-Apr-04 11:32 
GeneralRe: Benchmarks Pin
NormDroid20-Apr-04 21:33
professionalNormDroid20-Apr-04 21:33 
GeneralRe: Benchmarks Pin
Colin Angus Mackay20-Apr-04 22:37
Colin Angus Mackay20-Apr-04 22:37 
GeneralRe: Benchmarks Pin
Chester Ragel20-Apr-04 21:58
Chester Ragel20-Apr-04 21:58 
GeneralRe: Benchmarks Pin
jconwell20-Apr-04 5:44
jconwell20-Apr-04 5:44 
GeneralRe: Benchmarks Pin
Chester Ragel20-Apr-04 17:01
Chester Ragel20-Apr-04 17:01 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.