Java 为什么对于小型阵列,Arrays.copyOf的速度是System.arraycopy的2倍?
我最近在玩一些基准测试,发现了非常有趣的结果,我现在无法解释。以下是基准:Java 为什么对于小型阵列,Arrays.copyOf的速度是System.arraycopy的2倍?,java,arrays,performance,microbenchmark,Java,Arrays,Performance,Microbenchmark,我最近在玩一些基准测试,发现了非常有趣的结果,我现在无法解释。以下是基准: @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({"
@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);
}
}
这里有两件奇怪的事:
比Arrays.copyOf
快2倍 阵列(1,5,10大小)。但是,在大小为1000的大型阵列上System.arraycopy
的速度几乎慢了两倍。我都知道 方法是内在的,所以我期望有相同的性能。哪里 这种差异来自哪里李>Arrays.copyOf
- 单元素数组的手动复制比System.arraycopy快。我不清楚为什么。有人知道吗
VM版本:JDK 1.8.0131,VM 25.131-b11您的
SystemArrayCopy
基准在语义上与arraysCopyOf
不等价
如果你换个新的,它会是
System.arraycopy(ar, 0, result, 0, length);
与
随着这一变化,两个基准的性能也将变得相似
为什么第一种变体速度较慢
length
与ar.length
JVM需要执行额外的边界检查,并准备在length>ar.length
时抛出IndexOutOfBoundsException
-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函数的任何运行时调用。因为
copyOf
在内部使用arraycopy
,所以您的基准测试就是问题所在。@Andreas您的错误<代码>数组。copyOf是JVM固有的。一旦方法被JIT编译,数组中的Java代码就不会被执行。@Andreas您认为基准测试有什么特别的问题吗?它明智地避免了常见的基准测试陷阱。@apangin这是一个没有区别的区别。这同样适用于任何Java代码。这不会使任何任意方法成为“JVM内在方法”。@EJP好的,我会改写。JIT编译器不会查看Arrays.copyOf
的字节码,因为它对这种方法应该做什么有内部知识。ar.length和length
不一样吗?Math.min(ar.length,length)
有什么影响?@Boann它们是一样的,但JVM不知道这一点Math.min
让编译器知道System.arraycopy
永远不会抛出IndexOutOfBoundsException
。请解释。编译器从地上的一个洞里不知道System.arraycopy()
,除了它的名称、调用序列、结果和抛出的子句(实际上它没有)System.arraycopy()
在其第五个参数中只接收到Math.min()
的结果,并且不知道它是如何派生的。@EJP我不确定您指的是什么编译器,但HotSpot JIT编译器(实际上,它们都是-C1和C2)肯定是关于System.arraycopy()
和对IR节点图的调用。然后有助于减少这个图表。@EJP谁使这个术语消失了?“JIT’仍然在和由Oracle工程师正式使用。
System.arraycopy(ar, 0, result, 0, Math.min(ar.length, length));
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