Java 为什么克隆阵列这么慢?
这个测试Java 为什么克隆阵列这么慢?,java,performance,clone,Java,Performance,Clone,这个测试 for (;;) { int[] a = new int[10]; System.gc(); long t0 = System.currentTimeMillis(); for (int i = 0; i < 1000000; i++) { // int[] b = a.clone(); int[] b =
for (;;) {
int[] a = new int[10];
System.gc();
long t0 = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
// int[] b = a.clone();
int[] b = Arrays.copyOf(a, a.length);
}
System.out.println(System.currentTimeMillis() - t0);
}
(;;)的{
int[]a=新的int[10];
gc();
长t0=System.currentTimeMillis();
对于(int i=0;i<1000000;i++){
//int[]b=a.clone();
int[]b=Arrays.copyOf(a,a.length);
}
System.out.println(System.currentTimeMillis()-t0);
}
Arrays.copyOf显示约50毫秒,clone显示约160毫秒。克隆是一种特殊的本地复制方法,为什么它这么慢
我在HotSpot客户端JVM1.7.0_11-b21上运行了测试。请注意,当阵列的大小增加时,克隆和复制之间的差异消失。我在我的系统上运行了您的代码:它们之间几乎没有差异。两个时钟都在30毫秒左右。我的测试是在OpenJDK 7上进行的 为了证实这一点,我还通过卡尺进行了测试,并使用了更大的阵列来强调实际的复制性能:
public class Performance extends SimpleBenchmark {
final int[] source = new int[1000];
public int timeClone(int reps) {
int sum = 0;
for (int i = reps; i > 0; i--)
sum += source.clone().length;
return sum;
}
public int timeCopyOf(int reps) {
int sum = 0;
for (int i = reps; i > 0; i--)
sum += Arrays.copyOf(source,source.length).length;
return sum;
}
public static void main(String... args) {
Runner.main(Performance.class, args);
}
}
结果:
0% Scenario{vm=java, trial=0, benchmark=Clone} 2141.70 ns; σ=5416.80 ns @ 10 trials
50% Scenario{vm=java, trial=0, benchmark=CopyOf} 2168.38 ns; σ=1545.85 ns @ 10 trials
benchmark us linear runtime
Clone 2.14 =============================
CopyOf 2.17 ==============================
vm: java
trial: 0
根据请求,这里是阵列大小为10的:
0% Scenario{vm=java, trial=0, benchmark=Clone} 30.07 ns; σ=2.12 ns @ 10 trials
50% Scenario{vm=java, trial=0, benchmark=CopyOf} 29.34 ns; σ=161.38 ns @ 10 trials
benchmark ns linear runtime
Clone 30.1 ==============================
CopyOf 29.3 =============================
我发现了一篇很好的文章,解释了为什么克隆在这里很慢。简而言之,这是因为int[].clone只使用Object.clone,此方法在复制阵列之前进行两次检查: 1.检查实例是普通对象还是数组 2.检查数组是基元数组还是对象数组 我将这些检查添加到Arrrays.copyOf测试中
for (int i = 0; i < 1000000; i++) {
Class cls = a.getClass();
if (cls.isArray() && !cls.getComponentType().isAssignableFrom(Object.class)) {
int[] b = Arrays.copyOf(a, a.length);
}
}
for(int i=0;i<1000000;i++){
类cls=a.getClass();
if(cls.isArray()&&!cls.getComponentType().isAssignableFrom(Object.class)){
int[]b=Arrays.copyOf(a,a.length);
}
}
测试显示克隆和数组之间没有差异。如果它是用于阵列的专用版本,克隆会很快。
clone
过载(因为它必须同时处理指针和标量阵列),并且在标准Sun/Oracle实现中,可能未进行优化。我曾经研究过clone
的实现,这肯定会做得更好。还请注意,如果使用较大的数组(比如10000个元素),则数字可能会有所不同。下面是对您解释的一个bug的引用:--无论您的微基准多么糟糕-这显然是错误的,存在这种可能性,编译器将删除完整的循环。克隆的数组指向未使用的局部变量。我不确定字节码是否会包含该循环。我在HotSpot客户端JVM1.7.011上运行了测试-b21@ChristianKuetbachHotSpot不敢消除整个方法调用,因为它可能有副作用。@Marko你能在数组大小为10的情况下重新运行测试吗?这里有一些证据,无需测试:a)
那篇文章是关于Java 1.5的,是2006年的b)
即使考虑到他们的发现,也要注意他们的结论:与new
+arraycopy
相比,更喜欢clone
,因为代码更简单,复制性能相同。两个简单的if
检查不可能导致任何实际代码的性能下降。