java – System. Arraycopy is slow

I've been trying to measure the system arrayCopy vs Arrays. Copyof performance, in order to correctly select one of them Just for the benchmark, I added manual copy, and the result surprised me

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" );
    }
}

The results show that the execution rate of manual copying is about 30%, as shown in the figure below:

****************** 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

I'm really confused because I think (maybe I'll still do this) because it was born system Arraycopy is the best way to copy arrays, but I can't explain the result

Solution

In fact, the hotspot compiler is smart enough to expand and vectorize manual copy loops - which is why the resulting code looks well optimized

Why system Arraycopy slower? It is initially a native method, and you must pay for the native call until the compiler optimizes it into a JVM intrinsic

However, in testing, the compiler did not have the opportunity to do this optimization because the amplification method is not called many times (i.e. it is not considered hot)

I'll give you an interesting technique to force optimization Rewrite the magnification method as follows:

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;
}

An empty loop triggers a reverse counter overflow, which in turn triggers the compilation of the amplification method Empty loops are then eliminated from the compiled code, so it is harmless The current amplification method is about 1.5 times faster than the manual cycle!

What matters is system Arraycopy follows the new int [] In this case, hotspot can optimize the redundancy zeroing of the newly allocated array As you know, all Java objects must be zeroed immediately after creation However, if the compiler detects that the array is filled immediately after creation, it may eliminate zeroing and make the resulting code faster

Attachment: @ assylias' benchmark is good, but it is also because of system The fact that arraycopy is not internalized for large arrays In the case of small arrays, the arraycopy benchmark is called many times per second. JIT thinks it is very hot and can be optimized well However, for large arrays, each iteration is longer, so the number of repetitions per second is much less, and the JIT does not treat arraycopy as hot

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>