views:

517

answers:

9

While updating for loops to for-each loops in our application, I came across a lot of these "patterns":

for (int i = 0, n = a.length; i < n; i++) {
    ...
}

instead of

for (int i = 0; i < a.length; i++) {
    ...
}

I can see that you gain performance for collections because you don't need to call the size() method with each loop. But with arrays??

So the question arose: is array.length more expensive than a regular variable?

+2  A: 

It might be ever-so-slightly faster to store it in a variable. But I would be extremely surprised if a profiler pointed to it as being a problem.

At the bytecode level, getting the length of an array is done with the arraylength bytecode. I don't know if it is slower than an iload bytecode, but there shouldn't be enough difference to notice.

Michael Myers
+17  A: 

No, a call to array.length is O(1) or constant time operation.

Since the .length is(acts like) a public final member of array, it is no slower to access than a local variable. (It is very different from a call to a method like size())

A modern compiler is likely to optimize the call to .length right out anyway.

jjnguy
@jjnguy: Can't the call to `array.length` be O(1) and still take longer than `int len = array.length; i < len;`, which would also be O(1)?
Grant Wagner
Well, if you are worrying about such things you should be programming in C or C++ imho. (But, yeah, you may be right)
jjnguy
@jjnguy: Just being pedantic and pointing out that all O(1) are not created equal.
Grant Wagner
I added more info. Should make more sense now.
jjnguy
If the time difference between looking up the value in the direct variable call of .length vs. n is that much of a difference, then there might be something else wrong with the code. A profiler would help identify the hot spots. This smells to me more of optimization without representation, and I agree with jjnguy completely.
aperkins
@aperkins, glad to have someone on my side!
jjnguy
+7  A: 

I doubt there is any significant difference whatsoever, and even if there was, I would bet it's probably optimized away during compilation. You're wasting your time when you try to micro-optimize things like that. Make the code readable and correct first, then if you have a performance problem, use a profiler, then worry about choosing better data structures/algorithms if appropriate, then worry about optimizing the parts your profiler highlights.

IRBMe
+1. Unless you are doing something incredibly trivial inside the loop, whatever you *are* doing inside the loop is probably going to take longer than accessing `.length` by orders of magnitude.
Grant Wagner
+3  A: 

The length of an array is stored as a member variable of the array (not the same as an element) in Java, so fetching that length is a constant time operation, the same as reading a member variable from a regular class. Many older languages like C and C++ don't store the length as part of the array, so you'd want to store the length before the loop starts. You don't have to do that in Java.

Bill the Lizard
<nitpick> It's not exactly like a member variable; it actually has its own special bytecode. </nitpick>
Michael Myers
+1  A: 

This answer is from a C# point of view, but I would think the same applies to Java.

In C#, the idiom

for (int i = 0; i < a.length; i++) {    ...}

is recognized as iterating over the array, so the bounds check is avoided when accessing the array in the loop instead of with each array access.

This may or may not be recognized with code such as:

for (int i = 0, n = a.length; i < n; i++) {    ...}

or

n = a.length;

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

How much of this optimization is performed by the compiler vs. the JITter I don't know, and in particular if it's performed by the JITter I'd expect all 3 to generate the same native code.

However, the first form is also arguably more readable by people, so I'd say go with that.

Michael Burr
Yes, I think Java does the same optimization (at least more recent JVMs should).
Michael Myers
+4  A: 

I had a bit of time over lunch:

public static void main(String[] args) {
    final int[] a = new int[250000000];
    long t;

    for (int j = 0; j < 10; j++) {
        t = System.currentTimeMillis();
        for (int i = 0, n = a.length; i < n; i++) { int x = a[i]; }
        System.out.println("n = a.length: " + (System.currentTimeMillis() - t));

        t = System.currentTimeMillis();
        for (int i = 0; i < a.length; i++) { int x = a[i]; }
        System.out.println("i < a.length: " + (System.currentTimeMillis() - t));
    }
}

The results:

n = a.length: 672
i < a.length: 516
n = a.length: 640
i < a.length: 516
n = a.length: 656
i < a.length: 516
n = a.length: 656
i < a.length: 516
n = a.length: 640
i < a.length: 532
n = a.length: 640
i < a.length: 531
n = a.length: 641
i < a.length: 516
n = a.length: 656
i < a.length: 531
n = a.length: 656
i < a.length: 516
n = a.length: 656
i < a.length: 516

Notes:

  1. If you reverse the tests, then n = a.length shows as being faster than i < a.length by about half, probably due to garbage collection(?).
  2. I couldn't make 250000000 much larger because I got OutOfMemoryError at 270000000.

The point is, and it is the one everyone else has been making, you have to run Java out of memory and you still don't see a significant difference in speed between the two alternatives. Spend your development time on things that actually matter.

Grant Wagner
try java -Xmx512M to make the heap bigger...
wbkang
This matters in C/C++.
Floetic
@wbkang: Identifying that my default heap size was insufficient for a very large array wasn't really the point of the exercise.
Grant Wagner
@Brian: Does it always matter in C/C++? If you had a 1,000 iteration loop that performed a 1 second calculation on each iteration would you really be concerned that accessing the length of the array takes a few milliseconds longer one way over another? Your time would be much better spent optimizing the calculation that is taking 1 second 1,000 times. I understand what you meant to say is "sometimes this matters", but even for C/C++, it doesn't *always* matter. As I said, spend your development time on things that actually matter.
Grant Wagner
+1  A: 

It's not really hard to make a little experiment on this kind of thing.

public static void main(String[] args) throws Exception {
    int[] array = new int[10000000];

    int nothing=0;
    long time = System.currentTimeMillis();
    // iterating
    for(int i=0; i<array.length; i++) {
        nothing += 1;
    }
    System.out.println("1:time elapsed: " + (System.currentTimeMillis() - time) + "ms");

    time = System.currentTimeMillis();
    // iterating
    for(int i=0,n=array.length; i<n; i++) {
        nothing += 1;
    }
    System.out.println("2:time elapsed: " + (System.currentTimeMillis() - time) + "ms");

}

Both of them takes the same time. 1:time elapsed: 9ms 2:time elapsed: 9ms

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

As this is a common java idiom, it is a lot easier to read for other developers. Most importantly, it's not even an optimization. Don't do it.

wbkang
It *is* actually quite hard to do micro-benchmarking in Java with reasonable statistical confidence.
Christian Vest Hansen
IMO, unless there's a super obvious performance hit, optimizations that "obscure" the code should not be done, unless the bottleneck is properly profiled and identified.
wbkang
+1  A: 

Array.length is a constant and the JIT compiler should see through that in both instances. I would expect the resulting machine code to be the same in both cases. At least for the server compiler.

Christian Vest Hansen
+1  A: 

In that case, why don't you make an inverse loop:

for (int i = a.length - 1; i >= 0; --i) {
    ...
}

There are 2 micro optimizations here:

  • Loop reversal
  • Postfix decrement
instcode
why is "loop reversal" better? Because comparison to 0 is faster?
Stroboskop
Yes, I think so. Someone has made a benchmark for this: http://www.mkyong.com/java/reverse-loop-versus-forward-loop-in-performance-java/
instcode
And as to "why don't you" - because i want to keep the code simple. The reason i asked this question was merely to see if there's any sane reason to use the pattern described in the question.
Stroboskop