2

In Dov Bulka's Java Performance and Scalability: Volume 1, the author mentions that looping over an ArrayList with something such as

for (int i = 0; i < vector.size(); i++) {
    // do something that does not modify vector size...
}

is actually a minor optimization issue because of the constant computation of vector.size(), thus implying that something such as

int size = vector.size();    
for (int i = 0; i < size; i++) {
    // do something that does not modify vector size...
}

is actually slightly more efficient. As the book was written in 2000, the author was using the Sun 1.2.2 JDK.

Does this condition still hold for newer JDKs? Or is Java compilation now smart enough to remove these inefficiencies, despite how minor they may be?

EDIT: I don't worry about these tiny optimizations in my code; I was simply curious about the evolution of the JDK.

3
  • 9
    Unless you can prove that repeatedly calling size() is a bottleneck in your code, don't waste your time worrying about such micro-optimizations. In any case, that optimization is only safe under certain conditions (i.e. that no other thread can change the size of vector while you are in the loop). Commented Nov 15, 2016 at 13:40
  • 4
    Consider using a for-each loop and not worrying about the internals of the the List<>. In java 8 List#forEach(...) will hide it even more. Commented Nov 15, 2016 at 13:42
  • 2
    However, the fundamental rule of "don't do something in a loop unless you absolutely must do it in the loop" still applies. No matter how the technology evolves, doing less computation in a loop is a good thing. Commented Nov 15, 2016 at 13:49

4 Answers 4

3

Checking in a loop byte-code:

12: iload_3
13: aload_2
14: invokeinterface #4,  1            // InterfaceMethod java/util/List.size:()I
19: if_icmpge     31
22: iinc          1, 1
25: iinc          3, 1
28: goto          12

Putting it in a variable byte-code:

10: aload_2
11: invokeinterface #4,  1            // InterfaceMethod java/util/List.size:()I
16: istore_3
17: iconst_0
18: istore        4
20: iload         4
22: iload_3
23: if_icmpge     35
26: iinc          1, 1
29: iinc          4, 1
32: goto          20

Seems like it's invoking it every time, so actually putting it in a variable is faster, I wouldn't worry about it tho. Be aware that I'm newbie in byte-code and I may be completely wrong.

Sign up to request clarification or add additional context in comments.

1 Comment

The bytecode is 100% irrelevant, see my answer.
1

Does this condition still hold for newer JDKs? Or is Java compilation now smart enough to remove these inefficiencies, despite how minor they may be?

Considering the "Java compiler" javac, nothing has changed and most probably never will. It's not its job to do any optimizations. So looking at the generated bytecode is pointless.

Optimizations get done at runtime by the JIT compiler (Oracle Hotspot). It surely can inline such a trivial method and it most probably also can cache the size in a register so the memory access gets eliminated. For this, it needs to be able to inline everything into the method - otherwise there'd be no guarantee that vector.size does not change.

PS: The real performance problem may be the use of Vector, which is a pretty pointless class since years. Prefer ArraysList.

Comments

1

This is the implementation of size() int ArrayList class

/**
 * Returns the number of elements in this list.
 *
 * @return the number of elements in this list
 */
public int size() {
    return size;
}

In this case since it is stored in object's property, so its just a function call and returns the value of size(does not calculate it).So here it is just about preventing one function call.

It definitely makes sense to store size in a variable, if the size() method iterates over the list to calculate size, everytime it is called.

1 Comment

No. The method call gets inlined whenever this matters. There may be cases when this doesn't happen and I'm looking forward to see one real case.
1

Because size() is a method, if it is evaluated every time in the loop it will be slower than evaluating it once and storing it in a variable. The issue is not that it calculates the array size again; rather it is the overhead of calling a function at all. This will hurt performance regardless of what is in the method (though of course a long, slow, complicated function will hurt it more than a simple getter).

I was careful to say "if" it is evaluated every time because compilers may decide to inline the function call which would eliminate the overhead and the loop will be just as fast. This is the same as the for-each vs. generic for loop debate. If the for-each function calls are not inlined, it will be slower than a general for loop with no function calls.

There are definitely instances where this can make a big difference in performance so it is good to be aware of these subtleties. Real time signal processing algorithms that need to have high throughput are good examples of programs that could be sensitive to unnecessary overhead. Granted, those are not usually written in java but still, but it is good to know about these things.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.