I've been trying to measure the performance of the System.arrayCopy vs Arrays.copyOf in order to choose properly one of them. Just for the sake of benchmark I added manual copy as well and the result surprised me. Obviously I'm missing something really important, could you, please, tell me, what it is? The implementation is as follows (see first 4 methods).
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" );
}
}
The result shows that manual copy performed around 30% better, as shown 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 thought (and probably I still do) that System.arrayCopy
due to its nativity is the best possible way to copy an array, but I cannot explain this result.