gpt4 book ai didi

java - System.arrayCopy 很慢

转载 作者:塔克拉玛干 更新时间:2023-11-03 03:03:36 25 4
gpt4 key购买 nike

我一直在尝试衡量 System.arrayCopy 与 Arrays.copyOf 的性能,以便正确选择其中之一。为了基准测试,我也添加了手动副本,结果让我感到惊讶。显然我遗漏了一些非常重要的东西,你能告诉我它是什么吗?实现如下(见前4种方法)。

public class ArrayCopy {

public static int[] createArray( int size ) {
int[] array = new int[size];
Random r = new Random();
for ( int i = 0; i < size; i++ ) {
array[i] = r.nextInt();
}
return array;
}

public static int[] copyByArraysCopyOf( int[] array, int size ) {
return Arrays.copyOf( array, array.length + size );
}

public static int[] copyByEnlarge( int[] array, int size ) {
return enlarge( array, size );
}

public static int[] copyManually( int[] array, int size ) {
int[] newArray = new int[array.length + size];
for ( int i = 0; i < array.length; i++ ) {
newArray[i] = array[i];
}
return newArray;
}

private static void copyArray( int[] source, int[] target ) {
System.arraycopy( source, 0, target, 0, Math.min( source.length, target.length ) );
}

private static int[] enlarge( int[] orig, int size ) {
int[] newArray = new int[orig.length + size];
copyArray( orig, newArray );
return newArray;
}

public static void main( String... args ) {
int[] array = createArray( 1000000 );
int runs = 1000;
int size = 1000000;
System.out.println( "****************** warm up #1 ******************" );
warmup( ArrayCopy::copyByArraysCopyOf, array, size, runs );
warmup( ArrayCopy::copyByEnlarge, array, size, runs );
warmup( ArrayCopy::copyManually, array, size, runs );
System.out.println( "****************** warm up #2 ******************" );
warmup( ArrayCopy::copyByArraysCopyOf, array, size, runs );
warmup( ArrayCopy::copyByEnlarge, array, size, runs );
warmup( ArrayCopy::copyManually, array, size, runs );
System.out.println( "********************* test *********************" );
System.out.print( "copyByArrayCopyOf" );
runTest( ArrayCopy::copyByArraysCopyOf, array, size, runs );
System.out.print( "copyByEnlarge" );
runTest( ArrayCopy::copyByEnlarge, array, size, runs );
System.out.print( "copyManually" );
runTest( ArrayCopy::copyManually, array, size, runs );
}

private static void warmup( BiConsumer<int[], Integer> consumer, int[] array, int size, int runs ) {
for ( int i = 0; i < runs; i++ ) {
consumer.accept( array, size );
}
}

private static void runTest( BiConsumer<int[], Integer> consumer, int[] array, int size, int runs ) {
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
long currentCpuTime = threadMXBean.getCurrentThreadCpuTime();
long nanoTime = System.nanoTime();
for ( int i = 0; i < runs; i++ ) {
consumer.accept( array, size );
}
System.out.println( "-time = " + ( ( System.nanoTime() - nanoTime ) / 10E6 ) + " ms. CPU time = " + ( ( threadMXBean.getCurrentThreadCpuTime() - currentCpuTime ) / 10E6 ) + " ms" );
}
}

结果表明,手动复制的性能提高了 30% 左右,如下所示:

****************** warm up #1 ******************
****************** warm up #2 ******************
********************* test *********************
copyByArrayCopyOf-time = 162.470107 ms. CPU time = 153.125 ms
copyByEnlarge-time = 168.6757949 ms. CPU time = 164.0625 ms
copyManually-time = 116.3975962 ms. CPU time = 110.9375 ms

我真的很困惑,因为我认为(并且可能我仍然认为)System.arrayCopy 由于它的诞生是复制数组的最佳方式,但我无法解释这个结果.

最佳答案

实际上,HotSpot 编译器足够智能,可以展开和矢量化手动复制循环 - 这就是为什么结果代码看起来优化得很好。

为什么 System.arraycopy 变慢了?它本来是一个 native 方法,在编译器将其优化为 JVM intrinsic 之前,您必须为 native 调用付费。

但是,在您的测试中,编译器没有机会进行此类优化,因为 enlarge 方法没有被调用足够多的次数(即它不被认为是热的)。

我将向您展示一个强制优化的有趣技巧。重写enlarge方法如下:

private static int[] enlarge(int[] array, int size) {
for (int i = 0; i < 10000; i++) { /* fool the JIT */ }

int[] newArray = new int[array.length + size];
System.arraycopy(array, 0, newArray, 0, array.length);
return newArray;
}

一个空循环触发backedge counter溢出,进而触发enlarge方法的编译。然后从编译代码中消除空循环,因此它是无害的。现在 enlarge 方法比手动循环快 1.5 倍!

重要的是 System.arraycopy 紧跟在 new int[] 之后。在这种情况下,HotSpot 可以优化掉新分配数组的冗余归零。您知道,所有 Java 对象都必须在创建后立即归零。但就编译器检测到数组在创建后立即被填充而言,它可能会消除清零,从而使结果代码更快。

P.S. @assylias 的基准测试很好,但它也受到 System.arraycopy 未针对大型数组进行内化的事实的困扰。在小数组的情况下,每秒调用多次 arrayCopy 基准,JIT 认为它很热并且优化得很好。但对于大型数组,每次迭代都更长,因此每秒迭代次数要少得多,并且 JIT 不会将 arrayCopy 视为热数组。

关于java - System.arrayCopy 很慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40286540/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com