[英]Why does access to object fields is faster than to array in Java?
我期望数组是存储数据(读/写)的最有效方式,但是测试却相反。
public static class Store {
public int field1;
public int field2;
public int field3;
public int field4;
}
public static final int size = 5500000;
public static int[][] array = new int[4][size];
public static Store[] arrayStore = new Store[size];
...
for (int i = 0; i < size; ++i) {
sum += arrayStore[i].field1;
sum += arrayStore[i].field2;
sum += arrayStore[i].field3;
sum += arrayStore[i].field4;
}
VS:
for (int i = 0; i < size; ++i) {
sum += array[0][i];
sum += array[1][i];
sum += array[2][i];
sum += array[3][i];
}
[Java HotSpot(TM)SE(版本1.8.0_131-b11)32位]
我将 新int [size] [4] 更改为 新int [4] [size], 因为它占用的内存空间少得多
首先,在我的系统(Java 9.0.4 x64)上,所示的数组版本是对象版本的两倍。 因此,您的基准测试可能是错误的。
但是为了将一个苹果与另一个苹果进行比较,我们首先重构数组版本,以便沿着第一个维度大步前进,就像在对象版本中一样:
for (int i = 0; i < size; ++i) {
sum += array[i][0];
sum += array[i][1];
sum += array[i][2];
sum += array[i][3];
}
在这种情况下,由于经常检查微小的第二维,因此它的运行速度确实较慢。
请记住,Java中没有真正的多维数组。 new int[size][4]
确实是
int[][] array = new int[size][];
for (int i = 0; i < size; ++i) {
array[i] = new int[4];
}
您可以将第一个“列”维可视化为包含指向行的指针,每行一个数组对象。 因此,每行的大小并不是真正固定的,需要在运行时进行检查。
实际上,我们看到数组变体执行的指令几乎是指令的两倍:
那是因为所有边界检查。 这是为test2生成的JIT代码的一部分:
0x4c8847b add eax, dword ptr [r12+r8*8+0x14]
0x4c88480 add eax, dword ptr [r12+r8*8+0x18]
0x4c88485 add eax, dword ptr [r12+r8*8+0x1c]
0x4c8848a shl r11, 0x3
0x4c8848e mov edx, 0x1
0x4c88493 nop
0x4c8849c nop
0x4c884a0 mov r8d, dword ptr [r11+rdx*4+0x10]
0x4c884a5 mov ecx, dword ptr [r12+r8*8+0xc] # bounds checking #
0x4c884aa lea r10, ptr [r12+r8*8]
0x4c884ae test ecx, ecx # bounds checking #
0x4c884b0 jbe 0x4c88572
0x4c884b6 add eax, dword ptr [r12+r8*8+0x10]
0x4c884bb cmp ecx, 0x1 # bounds checking #
0x4c884be jbe 0x4c88589 # bounds checking #
0x4c884c4 add eax, dword ptr [r12+r8*8+0x14]
0x4c884c9 cmp ecx, 0x3 # bounds checking #
0x4c884cc jbe 0x4c885a1
0x4c884d2 mov r9d, dword ptr [r11+rdx*4+0x14]
0x4c884d7 mov ecx, dword ptr [r12+r9*8+0xc] # bounds checking #
0x4c884dc add eax, dword ptr [r12+r8*8+0x18]
0x4c884e1 add eax, dword ptr [r12+r8*8+0x1c]
0x4c884e6 mov ebx, edx
0x4c884e8 inc ebx
0x4c884ea lea r10, ptr [r12+r9*8]
0x4c884ee test ecx, ecx # bounds checking #
0x4c884f0 jbe 0x4c88574 # bounds checking #
0x4c884f6 add eax, dword ptr [r12+r9*8+0x10]
0x4c884fb cmp ecx, 0x1 # bounds checking #
0x4c884fe jbe 0x4c8858b
0x4c88504 add eax, dword ptr [r12+r9*8+0x14]
0x4c88509 cmp ecx, 0x3 # bounds checking #
0x4c8850c jbe 0x4c885a7 # bounds checking #
0x4c88512 add eax, dword ptr [r12+r9*8+0x18]
0x4c88517 add eax, dword ptr [r12+r9*8+0x1c]
0x4c8851c add edx, 0x2
0x4c8851f cmp edx, 0x53ec5f
0x4c88525 jl 0x4c884a0
0x4c8852b cmp edx, 0x53ec60
0x4c88531 jnl 0x4c88566
JVM一直在不断改进,因此至少在new int[size][4]
的情况下,最终将有可能对其进行优化。 现在,尽管使用多维数组时请记住这一点。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.