Why is the second cycle of the cycle always faster than the first?

I tried to find out if the for loop was faster than the foreach loop and used the System.Diagnostics classes to complete the task. During the test, I noticed that the cycle that I set first, always runs slower than the last. Can someone please tell me why this is happening? My code is below:

using System; using System.Diagnostics; namespace cool { class Program { static void Main(string[] args) { int[] x = new int[] { 3, 6, 9, 12 }; int[] y = new int[] { 3, 6, 9, 12 }; DateTime startTime = DateTime.Now; for (int i = 0; i < 4; i++) { Console.WriteLine(x[i]); } TimeSpan elapsedTime = DateTime.Now - startTime; DateTime startTime2 = DateTime.Now; foreach (var item in y) { Console.WriteLine(item); } TimeSpan elapsedTime2 = DateTime.Now - startTime2; Console.WriteLine("\nSummary"); Console.WriteLine("--------------------------\n"); Console.WriteLine("for:\t{0}\nforeach:\t{1}", elapsedTime, elapsedTime2); Console.ReadKey(); } } } 

Here is the result:

 for: 00:00:00.0175781 foreach: 00:00:00.0009766 
+5
source share
8 answers

Probably because classes (like Console) should be compiled by JIT the first time. You will get the best performance by calling all the methods (to JIT them (then warm, then up)), and then run the test.

As other users have indicated, 4 passes will never be enough to show you the difference.

By the way, the performance difference between for and foreach will be negligible, and the benefits of reading with foreach almost always outweigh any marginal performance advantage.

+16
source
  • I would not use DateTime to measure performance - try the Stopwatch class.
  • Measuring in just 4 passes will never give you a good result. Better to use> 100,000 passes (you can use an outer loop). Do not execute Console.WriteLine in your loop.
  • Even better: use a profiler (e.g. Redgate ANTS or possibly NProf)
+7
source

I'm not so much in C #, but when I remember correctly, Microsoft created the Just In Time compilers for Java. When they use the same or similar methods in C #, it would be natural that "some constructs going to the second are faster."

For example, it may be that the JIT-System sees that the loop is running and decides adhoc to compile the whole method. Therefore, when the second cycle is reached, it is still compiled and runs much faster than the first. But this is a rather simplified guess. Of course, you need a much deeper understanding of the C # runtime system to understand what is going on. It may also be that the RAM page is accessed first in the first cycle, and in the second it is still in the CPU cache.

Addon: Another comment that was made: that the output module may be JITed for the first time in the first loops of the loop for me more likely than my first assumption. Modern languages ​​are simply very difficult to learn what is done under the hood. In addition, this expression of mine fits into this assumption:

But you have terminal outputs in your loops. They make things even more difficult. It may also happen that it takes some time to open the terminal for the first time in the program.

+3
source

I just ran tests to get some real numbers, but at the same time Gaz beat me up to the answer - calling Console.Writeline splits on the first call, so you pay this cost in the first cycle.

For information only - using a stopwatch, not the date and time of tick measurement:

Without calling Console.Writeline, before the first cycle, time was

  for: 16802
 foreach: 2282

with a call to Console.Writeline they were

  for: 2729
 foreach: 2268

Although these results were not consistently repeated due to the limited number of runs, the magnitude of the difference was always approximately the same.


Edited code for reference:

  int[] x = new int[] { 3, 6, 9, 12 }; int[] y = new int[] { 3, 6, 9, 12 }; Console.WriteLine("Hello World"); Stopwatch sw = new Stopwatch(); sw.Start(); for (int i = 0; i < 4; i++) { Console.WriteLine(x[i]); } sw.Stop(); long elapsedTime = sw.ElapsedTicks; sw.Reset(); sw.Start(); foreach (var item in y) { Console.WriteLine(item); } sw.Stop(); long elapsedTime2 = sw.ElapsedTicks; Console.WriteLine("\nSummary"); Console.WriteLine("--------------------------\n"); Console.WriteLine("for:\t{0}\nforeach:\t{1}", elapsedTime, elapsedTime2); Console.ReadKey(); 
+3
source

The reason that the foreach version has several forms of overhead that are not in the for loop

  • Using IDisposable.
  • An additional method call for each element. Each item should be accessible under the hood using IEnumerator<T>.Current , which is a method call. Since it on the interface cannot be embedded. This means that the N-method calls, where N is the number of elements in the enumeration. The for loop uses and indexes
  • In the foreach loop, all calls go through the interface. In general, this is a bit slower than through a specific type

Please note that the above items are not necessarily huge costs. They are usually very low costs, which can contribute to a small difference in performance.

Also note, as Mehrdad noted, compilers and JITs can choose to optimize the foreach loop for certain well-known data structures such as an array. The end result can only be a for loop.

Note. For your overall performance test, a little more work is required to be precise.

  • You should use StopWatch instead of DateTime. This is much more accurate for performance tests.
  • You must run the test many times more than once
  • You need to perform a dummy run in each loop to fix problems that occur when you first run the JIT method. This is probably not a problem when all the code is in the same method, but it will not hurt.
  • You need to use no more than 4 values ​​in the list. Try 40,000 instead.
+2
source

You must use StopWatch to complete the time.

Technically, the for loop is faster. Foreach calls the MoveNext () method (creating a stack of methods and other overhead from the call) on an IEnumerable iterator when you only need to increment the variable.

+1
source

I do not understand why everyone says that for will be faster than foreach in this particular case. For a List<T> this is (about 2x slower to foreach through the list than for through via List<T> ).

In fact, foreach will be a little faster than for here. Since foreach in an array is essentially compiled into:

 for(int i = 0; i < array.Length; i++) { } 

Using .Length as a stopping criterion allows JIT to remove array bounds checks, as this is a special case. Using i < 4 makes JIT insert additional instructions to check each iteration, regardless of whether it is outside t212> outside the boundaries of the array and throws an exception if that is the case. However, with .Length it can guarantee that you will never go beyond the bounds of the array, so border checks will be redundant, which will make it faster.

However, in most cycles, the cycle overhead is negligible compared to working inside.

The discrepancy you see can only be explained by JIT.

+1
source

I wouldn’t read too much about this - this is not a good profiling code for the following reasons
1. DateTime is not intended for profiling. You should use a QueryPerformanceCounter or StopWatch that use processor hardware profile counters 2. Console.WriteLine is a device method, so subtle effects like buffering can occur to take into account 3. Running one iteration of each block of code will never give you exact results, because your processor does a lot of funky on-the-fly optimization, for example, outside of order execution and scheduling instructions
4. Most likely, the code that receives JITed for both blocks of code is pretty similar, so most likely it will be in the command cache for the second block of code

To get a better idea of ​​time, I did the following

  • Replaced Console.WriteLine with a mathematical expression (e ^ num)
  • I used QueryPerformanceCounter / QueryPerformanceTimer through P / Invoke
  • I ran each block of code 1 million times and then averaged the results

When I did this, I got the following results:

The for loop took 0.000676 milliseconds
The foreach loop took 0.000653 milliseconds

So foreach was very slightly faster, but not much

Then I did some further experiments and first ran the foreach block, and the second - the second. When I did this, I got the following results:

The foreach loop took 0.000702 milliseconds
The for loop took 0,000691 milliseconds

Finally, I ran both loops together twice ie for + foreach, then for + foreach again
When I did this, I got the following results:

Foreach loop took 0.00140 milliseconds
The for loop took 0.001385 milliseconds

So basically it seems to me that any code that you run secondly works very slightly faster, but not enough to make any difference.
--Edit--
Here are some useful links.
How to manage managed time code using QueryPerformanceCounter
Command cache
Order fulfillment

+1
source

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


All Articles