What is the Cost of Calling array.length
Asked Answered
B

8

62

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?

Becalm answered 30/7, 2009 at 18:9 Comment(1)
for readabilities sake - please use the second pattern or the for..each loop. Please!Atworth
K
82

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 JIT compiler is likely to optimize the call to .length right out anyway.

You can confirm this by either looking at the source code of the JIT compiler in OpenJDK, or by getting the JVM to dump out the JIT compiled native code and examining the code.

Note that there may be cases where the JIT compiler can't do this; e.g.

  1. if you are debugging the enclosing method, or
  2. if the loop body has enough local variables to force register spilling.
Kalk answered 30/7, 2009 at 18:12 Comment(10)
@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)?Shakespeare
Well, if you are worrying about such things you should be programming in C or C++ imho. (But, yeah, you may be right)Kalk
@jjnguy: Just being pedantic and pointing out that all O(1) are not created equal.Shakespeare
I added more info. Should make more sense now.Kalk
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.Enigmatic
@aperkins, glad to have someone on my side!Kalk
"A modern compiler is likely to optimize the call to .length right out anyway." [citation needed]Donnelldonnelly
@Donnelldonnelly - The source code of all OpenJDK JIT compilers since Java 6 is freely available for you to research this :-)Fabyola
@StephenC I wasn't the one to claim it. You're invited to prove it. Last time I checked, at least javac didn't optimize anything. I don't know much about JIT compilers.Donnelldonnelly
@Donnelldonnelly - I know. You were the one disputing it. I have given you the citation you requested. So that you can learn more about JIT compilers :-)Fabyola
S
20

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.

Shakespeare answered 30/7, 2009 at 18:39 Comment(5)
@wbkang: Identifying that my default heap size was insufficient for a very large array wasn't really the point of the exercise.Shakespeare
@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.Shakespeare
I am a bit disappointed that the compiler didn't realize that the entire loop has absolutely no effect and simply optimized it away.Proteiform
I call 150 ms a significant difference if you work with game ticks of 50ms :-) but then again, you'd never use such huge arrays.Guinea
This is a poor benchmark, and the results are not indicative of what would happen in real-life code. When I run it with Java 11, it looks like the loops do get optimized away by the JIT compiler. I got 40,5,15,0,0,0,0,0,0,0,0,0,0,0,0,0... Judging from the numbers you were getting, I would say that the method didn't get JIT compiled.Fabyola
B
12

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.

Bluebeard answered 30/7, 2009 at 18:13 Comment(1)
+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.Shakespeare
H
7

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.

Hetero answered 30/7, 2009 at 18:19 Comment(1)
<nitpick> It's not exactly like a member variable; it actually has its own special bytecode. </nitpick>Tartarus
T
3

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.

Tartarus answered 30/7, 2009 at 18:11 Comment(0)
N
3

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
Nussbaum answered 31/7, 2009 at 17:33 Comment(4)
why is "loop reversal" better? Because comparison to 0 is faster?Becalm
Yes, I think so. Someone has made a benchmark for this: mkyong.com/java/…Nussbaum
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.Becalm
@Nussbaum Postfix decrement is faster than postfix increment?Repletion
S
2

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.

Sprit answered 30/7, 2009 at 18:24 Comment(1)
Yes, I think Java does the same optimization (at least more recent JVMs should).Tartarus
E
1

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.

Echolocation answered 30/7, 2009 at 19:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.