📜 ⬆️ ⬇️

In response to the question why access to an element of an array is faster than access to the fields of an object

Recently, a respected lany wrote a wonderful post about mutable numbers in Java: http://habrahabr.ru/post/151887/
In the comment to his post, I mentioned that if performance is important, then you can replace the wrapper object with a one-element array; access to an array element is, by definition, faster than retrieving a value from an instance field.
I inherited this stereotype from Sun after reading the next performance white paper. It was written there that the fastest access to a local variable occurs, followed by a static field, then the array element closes the instance field list.
Fortunately, they did not believe me, and this was the reason for writing this article.
The article is not designed for juniors, the reader must know Java, ASM x86 and bytecode.


In order to understand whether or not access to an array element will be faster compared with access to an object field, you can:
  1. Write synthetic performance test
  2. Understand what is really worth of the array [0] and this.value

I chose the second path, from synthetic tests already sick. To begin with, we throw a test class.
package numbers; public class TestNumbers { public static void main(String[] args) { test(new IntNumField()); test(IntNumArray.create()); } private static final void test(int[] mutableInt2) { for (int index = 10000; index-- > 0;) { IntNumArray.inc(mutableInt2); } } private static final void test(IntNumField mutableInt1) { for (int index = 10000; index-- > 0;) { mutableInt1.inc(); } } public static final class IntNumField { protected int value; public int getValue() { return value; } public void setValue(int value) { this.value = value; } public final void inc() { this.value++; } } public static final class IntNumArray { public static final int[] create() { return new int[1]; } public static final int get(int[] array) { return array[0]; } public static final void set(int[] array, int value) { array[0] = value; } public static final void inc(int[] array) { array[0]++; } } } 


The IntNumField class works with an object field, and IntNumArray, as I suggested, with a single-element array. Each of the classes contains an inc method which increments our mutable by one. In test methods, a call to each version of inc is run 10,000 times - so that the method is compiled with guarantee.
')
But first, take a look at baytkod:
 ~$ javap -c numbers/TestNumbers$IntNumField public int getValue(); Code: 0: aload_0 1: getfield #18; //Field value:I 4: ireturn public void setValue(int); Code: 0: aload_0 1: iload_1 2: putfield #18; //Field value:I 5: return public final void inc(); Code: 0: aload_0 1: dup 2: getfield #18; //Field value:I 5: iconst_1 6: iadd 7: putfield #18; //Field value:I 10: return 


In the getter method, everything is simple, we load this and push the value value onto the stack. The rest is about the same, well, except that inc uses dup for optimization.
Now let's see what awaits us in the IntNumArray class:
 ~$ javap -c numbers/TestNumbers$IntNumArray public static final int get(int[]); Code: 0: aload_0 1: iconst_0 2: iaload 3: ireturn public static final void set(int[], int); Code: 0: aload_0 1: iconst_0 2: iload_1 3: iastore 4: return public static final void inc(int[]); Code: 0: aload_0 1: iconst_0 2: dup2 3: iaload 4: iconst_1 5: iadd 6: iastore 7: return 


Approximately the same, except that getfield is replaced by iconst_0 and iaload. Well, it’s still not clear yet, so let's take a look at the assembler that will generate HotSpot for inc methods using special parameters:
 ~$ java -XX:+UnlockDiagnosticVMOptions -XX:CompileThreshold=10000 -XX:+PrintAssembly numbers.TestNumbers # {method} 'inc' '()V' in 'numbers/TestNumbers$IntNumField' # [sp+0x20] (sp of caller) 0x02141a87: cmp 0x4(%ecx),%eax 0x02141a8a: jne 0x020dd100 ; {runtime_call} [Verified Entry Point] 0x02141a90: mov %eax,0xffffc000(%esp) 0x02141a97: push %ebp 0x02141a98: sub $0x18,%esp ;*aload_0 ; - numbers.TestNumbers$IntNumField::inc@0 (line 50) 0x02141a9b: mov 0x8(%ecx),%esi ;*getfield value ; - numbers.TestNumbers$IntNumField::inc@2 (line 50) 0x02141a9e: inc %esi 0x02141a9f: mov %esi,0x8(%ecx) ;*putfield value ; - numbers.TestNumbers$IntNumField::inc@7 (line 50) 0x02141aa2: add $0x18,%esp 0x02141aa5: pop %ebp 0x02141aa6: test %eax,0x1b0100 ; {poll_return} 0x02141aac: ret # {method} 'inc' '([I)V' in 'numbers/TestNumbers$IntNumArray' # parm0: ecx = '[I' # [sp+0x20] (sp of caller) 0x02141c80: mov %eax,0xffffc000(%esp) 0x02141c87: push %ebp 0x02141c88: sub $0x18,%esp ;*aload_0 ; - numbers.TestNumbers$IntNumArray::inc@0 (line 71) 0x02141c8b: cmpl $0x0,0x8(%ecx) ; implicit exception: dispatches to 0x02141cb7 0x02141c92: jbe 0x02141cc1 0x02141c98: mov 0xc(%ecx),%esi ;*iaload ; - numbers.TestNumbers$IntNumArray::inc@3 (line 71) 0x02141c9b: inc %esi 0x02141c9c: cmpl $0x0,0x8(%ecx) 0x02141ca3: jbe 0x02141ccd 0x02141ca9: mov %esi,0xc(%ecx) ;*iastore ; - numbers.TestNumbers$IntNumArray::inc@6 (line 71) 0x02141cac: add $0x18,%esp 0x02141caf: pop %ebp 0x02141cb0: test %eax,0x1b0100 ; {poll_return} 0x02141cb6: ret 


I had to trim both methods by throwing things like code alignment and a bunch of code in the [Exception Handler] section.
If you look closely, both methods use inc% esi to increment the values ​​on the stack. Differ only
 mov 0x8(%ecx),%esi ;*getfield value 

and
 cmpl $0x0,0x8(%ecx) ; implicit exception: dispatches to 0x02141cb7 jbe 0x02141cc1 mov 0xc(%ecx),%esi ;*iaload 

The first two lines in the second piece of code is a check that the index 0 does not exceed the bounds of the array (0 <array.length). It is because of it that it turns out that I was wrong, Worse, the exact same check takes place two lines below ... It is unlikely that it will be slower, but certainly it will not be faster than access to the object field.

Thank you apangin for having doubted, I think it is interesting not only for the two of us. You can safely use the class wrapper and all the charms of the PLO.

PS I still run the performance benchmark, no difference.

Source: https://habr.com/ru/post/152005/


All Articles