对于小型数组,为什么Arrays.copyOf比System.arraycopy快2倍?
我最近玩了一些基准测试,发现了非常有趣的结果,我现在无法解释。 这是基准:
@BenchmarkMode(Mode.Throughput) @Fork(1) @State(Scope.Thread) @Warmup(iterations = 10, time = 1, batchSize = 1000) @Measurement(iterations = 10, time = 1, batchSize = 1000) public class ArrayCopy { @Param({"1","5","10","100", "1000"}) private int size; private int[] ar; @Setup public void setup() { ar = new int[size]; for (int i = 0; i < size; i++) { ar[i] = i; } } @Benchmark public int[] SystemArrayCopy() { final int length = size; int[] result = new int[length]; System.arraycopy(ar, 0, result, 0, length); return result; } @Benchmark public int[] javaArrayCopy() { final int length = size; int[] result = new int[length]; for (int i = 0; i < length; i++) { result[i] = ar[i]; } return result; } @Benchmark public int[] arraysCopyOf() { final int length = size; return Arrays.copyOf(ar, length); } }
结果:
Benchmark (size) Mode Cnt Score Error Units ArrayCopy.SystemArrayCopy 1 thrpt 10 52533.503 ± 2938.553 ops/s ArrayCopy.SystemArrayCopy 5 thrpt 10 52518.875 ± 4973.229 ops/s ArrayCopy.SystemArrayCopy 10 thrpt 10 53527.400 ± 4291.669 ops/s ArrayCopy.SystemArrayCopy 100 thrpt 10 18948.334 ± 929.156 ops/s ArrayCopy.SystemArrayCopy 1000 thrpt 10 2782.739 ± 184.484 ops/s ArrayCopy.arraysCopyOf 1 thrpt 10 111665.763 ± 8928.007 ops/s ArrayCopy.arraysCopyOf 5 thrpt 10 97358.978 ± 5457.597 ops/s ArrayCopy.arraysCopyOf 10 thrpt 10 93523.975 ± 9282.989 ops/s ArrayCopy.arraysCopyOf 100 thrpt 10 19716.960 ± 728.051 ops/s ArrayCopy.arraysCopyOf 1000 thrpt 10 1897.061 ± 242.788 ops/s ArrayCopy.javaArrayCopy 1 thrpt 10 58053.872 ± 4955.749 ops/s ArrayCopy.javaArrayCopy 5 thrpt 10 49708.647 ± 3579.826 ops/s ArrayCopy.javaArrayCopy 10 thrpt 10 48111.857 ± 4603.024 ops/s ArrayCopy.javaArrayCopy 100 thrpt 10 18768.866 ± 445.238 ops/s ArrayCopy.javaArrayCopy 1000 thrpt 10 2462.207 ± 126.549 ops/s
所以这里有两件奇怪的事情:
- 对于小型数组(1,5,10大小),Arrays.copyOf比
System.arraycopy
快2倍。 但是,在大型1000arrays上,Arrays.copyOf
几乎慢了2倍。 我知道这两种方法都是内在的,所以我期望性能相同。 这种差异来自哪里? - 1元素数组的手动复制比
System.arraycopy
快。 我不清楚为什么。 有人知道吗?
VM版本:JDK 1.8.0_131,VM 25.131-b11
您的SystemArrayCopy
基准测试在语义上不等于arraysCopyOf
。
如果你更换,它将是
System.arraycopy(ar, 0, result, 0, length);
同
System.arraycopy(ar, 0, result, 0, Math.min(ar.length, length));
通过这种改变,两个基准的性能也将变得相似。
为什么第一个变体会变慢?
- 在不知道
length
与ar.length
关系时,JVM需要执行额外的边界检查,并准备在length > ar.length
时抛出IndexOutOfBoundsException
。 -
这也打破了优化以消除冗余归零。 您知道,每个已分配的数组必须用零初始化。 但是,如果JIT在创建后立即填充数组,则可以避免归零。 但是
-prof perfasm
清楚地表明原始的SystemArrayCopy
基准测试花费了大量时间来清除分配的数组:0,84% 0x000000000365d35f: shr $0x3,%rcx 0,06% 0x000000000365d363: add $0xfffffffffffffffe,%rcx 0,69% 0x000000000365d367: xor %rax,%rax 0x000000000365d36a: shl $0x3,%rcx 21,02% 0x000000000365d36e: rep rex.W stos %al,%es:(%rdi) ;*newarray
对于小型数组,手动复制更快,因为与System.arraycopy
不同,它不会对VM函数执行任何运行时调用。