5

I generally write

for (int i = 0, n = someMethod(); i < n; i++)

in preference to

for (int i = 0; i < someMethod(); i++)

to avoid someMethod() being computed repeatedly. However I'm never really sure when I need to do this. How clever is Java at recognising methods that will give the same result every time and only need to be executed once at the beginning of a loop?

therealrootuser
  • 10,215
  • 7
  • 31
  • 46
Paul Boddington
  • 37,127
  • 10
  • 65
  • 116
  • why can't you do like `int n = someMethod();' and then use n later in loop? – Adi Aug 28 '14 at 03:12
  • 1
    I think http://stackoverflow.com/questions/6093537/for-loop-optimization answers this question. – trooper Aug 28 '14 at 03:13
  • Adi, that's basically what I've done in my first example. My question is when is it necessary to compute n first. I'm sure there are some instances where the second way I've written it gets treated like the first anyway, but I could be wrong about this. – Paul Boddington Aug 28 '14 at 03:14
  • Even if it gave the same result each time, can Java be SURE the method doesn't do something else (eg logging). I think it would have to call each time. – racraman Aug 28 '14 at 03:15
  • Your first style is better than the examples in some of the answers where the upper bound is declared outside the loop. Keeping variables in the tightest scope possible makes code easier to understand. – erickson Aug 28 '14 at 03:59
  • +1 I did not realize you could define multiple variables at once like in the first for loop. – Adam Jensen Aug 28 '14 at 04:15

2 Answers2

3

I believe the onus is on you, as the programmer, to identify cases where the upper bound of your for-loop needs to be calculated on the fly and address them accordingly.

Assuming that the value of n does not depend upon some operation that is performed within the loop, personally, I would prefer it written as

int n = someMethod();
for (int i = 0; i < n; i++);

because it preserves the most common style of for-loop while unambiguously defining the upper bound.

x4nd3r
  • 855
  • 1
  • 7
  • 20
  • 2
    I agree that the onus is on me. I was only really asking out of academic interest; I always compute n first. I'd still always prefer to write it like I did in my first example to limit the scope of n to the loop. – Paul Boddington Aug 28 '14 at 03:43
  • @Paul Completely understandable. Regarding scope: this is situational for me, but the enhanced readability usually wins out. – x4nd3r Aug 28 '14 at 04:06
1

The JIT, as far as I can tell, will only detect this if it's a fairly simple inlineable method. That being said it's very easy for a programmer to detect these cases and is a hard problem for a JIT compiler to detect. Preferably you should use final int to cache results from large methods, as the JIT can very easily detect that value can't change and can even remove array access checks to speed up loops.

Something like

int[] arr = new int[ 10 ];
for( int i = 0; i < arr.length; i++ ) {
    //...
}

or

List< String > list = Arrays.asList( new String[] { ... } );
for( int i = 0; i < list.size(); i++ ) {
    //...
}

can probably be very easily optimized by the JIT. Other loops that call large or complicated methods can't easily be proven to always return the same value, but methods like size() probably could be inlined or even removed completely.

Finally with for-each loops on arrays. They are decayed to the first loop I posted in the case of arrays, and can also easily be optimized to produce the quickest loop. Although for-each loops on non-arrays, I prefer to avoid when it comes to quick loops, as they decay into Iterator loops and not the second loop I posted. That is not true for LinkedList because an Iterator is faster than using get() due to O( n ) traversal.

This is all speculation on what the JIT could do to optimize a loop. It's important to know that the JIT will only optimize something it can prove will not change the resulting effects. Keeping things simple will make the JIT's job much easier. Like using the final keyword. Using final on values or on methods allows the JIT to easily prove that won't change and can inline like crazy. That's the JIT's most important optimization, inlining. Make that job easy and the JIT will help you out in a big way.

Here is a link discussing loop optimizations where the JIT can't always optimize a loop if it can't prove it's optimization won't change anything.

Community
  • 1
  • 1
Smith_61
  • 2,078
  • 12
  • 12
  • Thank you, that's a very thorough answer. I'd be really interested to know if there are any other common methods apart from size() that get inlined in this way. In your second example, if the first line had been List list = Arrays.asList(arr); instead, technically size() could change in the loop. Would the JIT be able to recognise when arr isn't being modified in the loop and inline size() in this case? – Paul Boddington Aug 28 '14 at 03:37
  • @PaulBoddington Technically arr.length could change at any time too. Since List.get() and List.size() on ArrayList are basically just wrappers around a single array access. I would assume that once that is inlined it will look a lot like the first loop. Which might just be enough for the JIT to continue optimizing it. – Smith_61 Aug 28 '14 at 03:40
  • @PaulBoddington I guess more what I am trying to say here is that the JIT is much better at inlining. That is its primary optimization. It's go to move, and it is very effective. Detecting non-changing values when it comes to loops, is mostly beneficial for removing out of bounds checks. Which is probably the second most beneficial optimization the JIT can perform. – Smith_61 Aug 28 '14 at 03:43
  • I've just remembered that the length of an array is fixed, so previous comments are incorrect. – Paul Boddington Aug 28 '14 at 03:49
  • @PaulBoddington but the reference could change. I mean that is easily detectable but it's the same thing as adding to an ArrayList once all the abstraction is removed. – Smith_61 Aug 28 '14 at 03:50