The "Performance Tips" section in the Android documentation has a pretty bold claim:
one()
is faster. It pulls everything out into local variables, avoiding the lookups. Only the array length offers a performance benefit.
where it refers to this code snippet:
int len = localArray.length;
for (int i = 0; i < len; ++i) {
sum += localArray[i].mSplat;
}
This surprised me a lot because localArray.length
is just accessing an integer and if you'd use an intermediate variable, you'd have to do that exact same step again. Are we really saying that an intermediate variable that only has to go to x
instead of y.x
is faster?
I took a look over at this question which is about the same idea but uses an arraylist and its subsequent .size()
method instead. Here the concensus seemed to be that there would be no difference since that method call is probably just going to be inlined to an integer access anyway (which is exactly the scenario we have here).
So I took to the bytecode to see if that could tell me anything.
Given the following source code:
public void MethodOne() {
int[] arr = new int[5];
for (int i = 0; i < arr.length; i++) { }
}
public void MethodTwo() {
int[] arr = new int[5];
int len = arr.length;
for (int i = 0; i < len; i++) { }
}
I get the following bytecode:
public void MethodOne();
Code:
0: iconst_5
1: newarray int
3: astore_1
4: iconst_0
5: istore_2
6: iload_2
7: aload_1
8: arraylength
9: if_icmpge 18
12: iinc 2, 1
15: goto 6
18: return
public void MethodTwo();
Code:
0: iconst_5
1: newarray int
3: astore_1
4: aload_1
5: arraylength
6: istore_2
7: iconst_0
8: istore_3
9: iload_3
10: iload_2
11: if_icmpge 20
14: iinc 3, 1
17: goto 9
20: return
They differ in the following instructions:
Method one
6: iload_2
7: aload_1
8: arraylength
9: if_icmpge 18
12: iinc 2, 1
15: goto 6
18: return
Method two
9: iload_3
10: iload_2
11: if_icmpge 20
14: iinc 3, 1
17: goto 9
20: return
Now, I'm not 100% sure how I have to interpret 8: arraylength
but I think that just indicates the field you're accessing. The first method loads the index counter and the array and accesses the arraylength
field while the second method loads the index counter and the intermediate variable.
I benchmarked the two methods as well with JMH (10 warmups, 10 iterations, 5 forks) which gives me the following benchmarking result:
c.m.m.Start.MethodOne thrpt 50 3447184.351 19973.900 ops/ms
c.m.m.Start.MethodTwo thrpt 50 3435112.281 32639.755 ops/ms
which tells me that the difference is negligible to inexistant.
On what is the Android documentation's claim that using an intermediate variable in a loop condition, based?