java – System.arrayCopy很慢

前端之家收集整理的这篇文章主要介绍了java – System.arrayCopy很慢前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我一直在努力测量System.arrayCopy vs 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,target,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,runs );
        warmup( ArrayCopy::copyManually,runs );
        System.out.println( "****************** warm up #2 ******************" );
        warmup( ArrayCopy::copyByArraysCopyOf,runs );
        System.out.println( "********************* test *********************" );
        System.out.print( "copyByArrayCopyOf" );
        runTest( ArrayCopy::copyByArraysCopyOf,runs );
        System.out.print( "copyByEnlarge" );
        runTest( ArrayCopy::copyByEnlarge,runs );
        System.out.print( "copyManually" );
        runTest( ArrayCopy::copyManually,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[],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更慢?它最初是一种本机方法,您必须支付本机调用,直到编译器将其优化为JVM内在函数.

然而,在测试中,编译器没有机会进行这种优化,因为放大方法不被称为很多次(即不被认为是热).

我会给你一个有趣的技巧来强制优化.重写放大法如下:

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,newArray,array.length);
    return newArray;
}

空循环触发反向计数器溢出,这反过来触发了放大方法的编译.然后从编译的代码中消除空循环,因此它是无害的.现在的放大方法比手动循环快约1.5倍!

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

附: @assylias’的基准是好的,但也是因为System.arraycopy并没有为大数组内化的事实.在小数组的情况下,ArrayCopy基准测试每秒调用多次,JIT认为它很热,并能很好地优化.但是,对于大型阵列,每次迭代都会更长,因此每秒重复次数少得多,JIT不会将arrayCopy视为热.

猜你在找的Java相关文章