Loop is much faster than a for loop than a while loop, and then when the statement changes, the while loop is faster. What's happening?

Out of interest, I tested if there was a difference in the for loop, and the while loop did the same. What makes the while loop take about 2-2.5 seconds on my computer (AMD Phenom II X6 1090T @ 3.20 GHz) than the for loop? Don't they do the same? Do you get similar results?

Also, when I replace the operator x = null; of loops with only an empty statement, the while loop will be significantly faster. What's going on here?

Of course, the number of iterations is quite large, but isn’t the difference very significant?

 static void Main(string[] args) { String x; const Int64 FIVE_BN = 5000000000; Int64 i = 0; DateTime start = DateTime.Now; for (; FIVE_BN > i; i++) x = null; //Replace with only ; in both loops and the for loop is faster Console.Out.WriteLine(FIVE_BN.ToString() + " times (for): " + (DateTime.Now - start)); i = 0; start = DateTime.Now; while(FIVE_BN > i++) x = null; //Replace with only ; in both loops and the for loop is faster Console.Out.WriteLine(FIVE_BN.ToString() + " times (while): " + (DateTime.Now - start)); Console.Read(); return; } 
+4
source share
3 answers

Although this is completely micro-optimization, it will never be a performance bottleneck. Interestingly, the two are actually different, I wonder when you retrieve the methods as in loops with VS2010, I get the following:

 private static String forLoop(ref Int64 i) { String x; for (; FIVE_BN > i; i++) x = null; //Replace with only ; in both loops and the for loop is faster return x; } private static void whileloop(ref String x, ref Int64 i) { while (FIVE_BN > i++) x = null; //Replace with only ; in both loops and the for loop is faster } 

And this is quite interesting ... it shows that the two functions are really different.

now that we are replacing logic in a loop ; , instead, we get the following extracted methods:

 private static Int64 forLoopShort(Int64 i) { for (; FIVE_BN > i; i++) ; //Replace with only ; in both loops and the for loop is faster return i; } private static Int64 whileLoopShort(Int64 i) { while (FIVE_BN > i++) ; //Replace with only ; in both loops and the for loop is faster return i; } 

Which indicates why loops mostly work with this configuration.

To understand how they differ when embedding (and are not retrieved into methods), we need to look at how the optimized CLR code looks (although the optimizer can actually remove any significant differences between the two functions). This is something for later editing.

Edit:

CIL shows the differences:

The For loop has .maxstack 2 , but the while loop has .maxstack 4 , otherwise there is a slight difference in the order of operations because the increment for while occurs at the beginning of the loop, but for operation is performed at the end of the loop (change the contents of the loop on Console.WriteLine(i) and see that the While loop will print from 1, but the For loop will print from 0 (both do the same number of loop iterations, though).

When the contents of the loop are only equal ; , both loops in CIL are reduced by 2 lines, and the following lines are deleted (for both loops):

 IL_0006: ldnull IL_0007: stloc.0 

However, when we create in the release, the code is very different:

The difference between x = null; and ; is nothing for any of the loops, as the optimizer has noticed that the value never changes to nonempty.

The difference between the optimized while and while loops is as follows:

CIL for loop:

 IL_0000: ldc.i4.0 IL_0001: conv.i8 IL_0002: stloc.0 IL_0003: br.s IL_000a IL_0005: ldloc.0 IL_0006: ldc.i4.1 IL_0007: conv.i8 IL_0008: add IL_0009: stloc.0 IL_000a: ldc.i8 0x12a05f200 IL_0013: ldloc.0 IL_0014: bgt.s IL_0005 IL_0016: ret 

And the CIL while :

 IL_0000: ldc.i4.0 IL_0001: conv.i8 IL_0002: stloc.0 IL_0003: ldc.i8 0x12a05f200 IL_000c: ldloc.0 IL_000d: dup IL_000e: ldc.i4.1 IL_000f: conv.i8 IL_0010: add IL_0011: stloc.0 IL_0012: bgt.s IL_0003 IL_0014: ret 

So, we see that the optimized while loop is faster than the for loop by 2 operations, however it uses more stack space.

The difference between the two seems completely related to the difference in where i++ happens.

In fact, this is confirmed by the creation of a new method:

 private static void forLoopVeryShort() { string x; Int64 i = 0; for (; FIVE_BN > i++;) ; //Replace with only ; in both loops and the for loop is faster } 

The CIL code for this for method when building (in release or debugging) is identical to the while code.

There is your difference. For loops, it executes in exactly the same way as for loops when they perform the same behavior. The difference you noticed is entirely related to running the code in debugging, not release, in combination with JIT, which is not always effective as an optimizer for release code.

I liked this question, I learned something from it; I hope others do. +1

+11
source

You probably want to rip out cordbg (and turn on all JIT optimizations carefully) to look at the generated native code to determine exactly why this is happening ... but why bother? In real code, the difference will not be significant, because you will do the real work in a loop.

Micro-optimization of a completely unrealistic code is not a fruitful exercise, IMO. Even micro-optimizing real code is usually not fruitful unless you have already confirmed that this is a bottleneck.

+8
source

For information - it is impossible to reproduce:

with purpose:

 5000000000 times (for): 00:00:15.0488608 5000000000 times (while): 00:00:12.7107270 

using a ;

 5000000000 times (for): 00:00:15.0558611 5000000000 times (while): 00:00:12.7297281 

(here I start in release mode, outside the debugger, etc.)

It is possible that this is specific to the Framework (I am using 4.0.30319.488, x64) or processor-specific (I am using Intel i7 4x2.67GHz (plus HT)), but the first one I guess is the test is running.

+3
source

Source: https://habr.com/ru/post/1390014/


All Articles