Caching Variables in the `for` Loop

I read tips on improving Javascript performance, and one tip said was to cache all the variables (which don't change) in the loop comparator instruction, and I was wondering if this applies to .NET.

Assuming I had a simple loop cycle, which of the following will be faster or will they be the same?

No cache:

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

With cache:

 for (int i = 0, count = someArray.Length; i < count; i++) { } 

According to the article "caching", the Length value cuts out one operation in a loop, because it performs access to local variables faster than access to object members. Is it really faster to declare a local variable compared to just member access? Does the compiler approach this and automatically cache the value? Are there any negatives in declaring a local variable for member access?

Although speed is probably a key factor here, its not the only one. Perhaps my next question will be more efficient. Which uses less memory allocations? Which performs less stack manipulation? etc...

From the comments, it seems that access to the strings is an array rather fast. Suppose I use IList<> instead. Will caching Count values โ€‹โ€‹be faster than getting each iteration?

+6
source share
4 answers

In a compiled language, all you do is premature optimization . I assume that the interpreted language may save a little, but even there it seems that your gain will be so minimal that (in my experience) an unusual way to code a for loop.

To answer your question directly in C #, no, the compiler doesnโ€™t optimize anything by caching. I could easily create a new array with a new length during the loop. That way, it will load the length of the array every time it evaluates a stop condition. Or, even worse, I can't use the โ€œtraditionalโ€ style stopping conditions and maybe you will need to evaluate the function you need to know to stop.

However, here is a simple program:

 static void Main( string[] args ) { int[] integers = new int[] { 1, 2, 3, 4, 5 }; for( int i = 0; i < integers.Length; i++ ) { Console.WriteLine( i ); } } 

And here is IL (with nops removed):

 IL_000d: call void [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype [mscorlib]System.RuntimeFieldHandle) IL_0012: stloc.0 IL_0013: ldc.i4.0 IL_0014: stloc.1 IL_0015: br.s IL_0024 IL_0018: ldloc.1 IL_0019: call void [mscorlib]System.Console::WriteLine(int32) IL_0020: ldloc.1 IL_0021: ldc.i4.1 IL_0022: add IL_0023: stloc.1 IL_0024: ldloc.1 IL_0025: ldloc.0 IL_0026: ldlen IL_0027: conv.i4 IL_0028: clt IL_002a: stloc.2 IL_002b: ldloc.2 IL_002c: brtrue.s IL_0017 

The key answer to your question here is that it pushes the array to location 0 on the stack, then at run time IL_0026 to call the length of the array, IL_0028 does less than the comparison, and finally going to IL_0017 if the estimate is correct.

By caching the length of the array, all you save is a call to ldlen and stloc. The ldlen statement should be quick, since getting the length of the array is not too long.

EDIT:

The main difference from the list is as follows:

 IL_002b: callvirt instance int32 class [mscorlib]System.Collections.Generic.List`1<int32>::get_Count() 

callvirt will take longer, but this whole function realistically returns a private variable.

You would be better off worrying about things that take milliseconds - for example, database calls or optimizing SQL queries so that they are faster, etc., than trying to shave off individual IL operations.

+2
source

I doubt it will make your code faster and can make it slower. Getting the length of the array is really cheap (probably an L1 hit), and calculating the length in advance can ruin the elimination of the associated JIT check.

In C #: entry: array [i] is actually more like a letter: if (i> = array.Length) quit ...; array [i]

The CLR authors spent a lot of time making JIT really good at eliminating these checks when it is not needed. Writing your loops in a second style may make the CLR unable to eliminate validation.

0
source

I once tried caching against array.Length. Array.Length was faster. I think this is because of the internal structure of the virtual machine and the "security". You are using .Length notation, you believe that it will never overflow the array. With a variable, it is unknown and does additional tests. The assembly code looks one way, but the internal behavior of the virtual machine is different.

But, on the other hand, you are doing premature optimization.

0
source

Just to give a different answer. Caching length values โ€‹โ€‹may help you in some specific cases.

In my case, we used sample code with selenium for ui tests. The code looked like this:

 for (int i = 0; i < Grid.VisibleRows.Length; i++) { // do something } 

Our grid was just a class representing an html table. VisibleRows received IWebElement[] , and then used the Length property. Each iteration involved moving to the user interface and retrieving all the lines.

Of course, another implementation may simply be to go to the user interface once and get the length (instead of counting the lines in memory), but still moving the code from the for loop means improving performance - I have reduced the number from circular transitions to the user interface from many to one.

So, the code now looks like this:

 var rowsLength = Grid.VisibleRows.Length; for (int i = 0; i < rowsLength ; i++) { // do something } 

Manual caching has been developed for us. Using a stopwatch, we checked that we were able to reduce the execution time of this part of the code from 32993 ms to 12020 MS

0
source

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


All Articles