3

I know that in the following code array 'Length' property is not called on every loop iteration because Jit compiler is clever enough to recognize it as property (not method) and optimize the code to call it only once storing internally the value in the temporary variable:

Int32[] myArr = new Int32[100];
for(Int32 index = 0; index < myArr.Length; index++) {
// Do something with the current item
}

So there is no need for developer to trying to optimize this by caching the length to a local variable. My question: is it true for all collection types in .Net? For instance, suppose I have a List and call 'Count' property in for loop. Shouldn't I optimize this?

user2598794
  • 697
  • 2
  • 10
  • 23
  • 4
    What you know isn't right. Property getters are not required to return the same value every time they're called, so the jitter won't assume that they return the same value every time they're called. Even if properties were required to do so, there's nothing preventing `myArr` from being re-assigned within the loop. While the jitter might be able to optimise what you have in your question, the reasons why it will do so are not the reasons you give. –  Apr 18 '16 at 13:27
  • 2
    You're assumption is incorrect. Also properties are evaluated always because they could have changed on every iteration. But on properties the costs are (should be) almost 0, so don't micro-optimize. Most likely a duplicate: http://stackoverflow.com/questions/3375521/are-the-limits-of-for-loops-calculated-once-or-with-each-loop – Tim Schmelter Apr 18 '16 at 13:27
  • 1
    Yes, the IL generated for iterating over an array like that will issue the IL `ldlen` instruction for each iteration. – Matthew Watson Apr 18 '16 at 13:33
  • Performed a quick test: with local variable and without. For 100 000 elements in array I got next results: without local variable: 8590 milliseconds, using local variable for length: 9151 milliseconds. So without caching it works even faster. – user2598794 Apr 18 '16 at 14:05
  • @hvd: **Length is not a property of an array**. There is a special IL instruction to fetch the length of an array, and the jitter knows that it will always return the same value. **The jitter also knows whether there is an assignment in the loop**, and C# makes it easier than, say, C++ to detect aliasing. – Eric Lippert Apr 18 '16 at 15:12
  • @EricLippert The point of my comment wasn't whether the OP's code would or wouldn't be optimised, it was that the OP's reasoning that `Length` is a property, therefore the access can be optimised, was flawed. You're reinforcing that by showing two things the jitter needs to know beforehand. That said, `Length` certainly is a property of an array. Just try it: `typeof(int[]).GetProperty("Length")`. You're right that the compiler bypasses the property accessor because it has built-in knowledge of how to get an array's length, but that doesn't change the fact that it's a property. –  Apr 18 '16 at 17:35
  • @hvd: OK, sure, I misspoke. From the perspective of the type system Length is certainly a property. From the perspective of the jitter though, which is what is doing the optimization, it is very, very special. – Eric Lippert Apr 18 '16 at 18:03

1 Answers1

8

I know that in the following code array 'Length' property is not called on every loop iteration because Jit compiler is clever enough to recognize it as property (not method)

Though Length is, from the perspective of the type system a property, from the perspective of the jitter it is a special instruction just for loading the length of an array. That's the thing that enables the jitter to perform this optimization.

I note also that there is a larger optimization which you failed to mention. Checking the length is cheap. The larger optimization here is that the jitter can elide checks on the index operation into the array because it knows that the loop variable will always be in the bounds of the array. Since those checks can throw exceptions, by eliding them the jitter makes it easier to analyze the control flow of the method, and can therefore potentially do even better optimizations to the rest of the method.

is it true for all collection types in .Net?

The jitter is permitted to make that optimization for other collection types if it can prove that doing so is correct. Whether it actually does so or not you can test using science; observe the code generated by the jitter and see if it has this optimization. My guess would be that the jitter does not have this optimization.

For instance, suppose I have a List and call 'Count' property in for loop. Shouldn't I optimize this?

Now we come to the real crux of the question. Absolutely you should not optimize this. Optimization is incredibly expensive; your employer pays you for every minute you're spending optimizing your code, so you should be optimizing the thing that produces the biggest user-observable win. Getting the count of a list takes nanoseconds. There is no program in the world whose success in the marketplace was determined by whether or not someone removed a couple nanoseconds from a loop that checked a list count unnecessarily.

The way to spend your time doing performance optimizations is first have a customer-focused goal. That's what lets you know when you can stop worrying about performance and spend your money on something more important. Second, measure progress against that goal every day. Third, if and ONLY if you are not meeting your goal, use a profiler to determine where the code is actually not sufficiently performant. Then optimize the heck out of that thing, and that thing alone.

Nano-optimizations are not the way to engineer performance. They just make the code harder to read and maintain. Use a good engineering discipline when analyzing performance, not a collection of tips and tricks.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067