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
int[] myInterger = new int[1];
int total = 0;
for(int i = 0; i < myInterger.Length; i++)
{
total += myInterger[i];
}
foreach
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
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
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!
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.