Reputation: 1888
In a previous question Why clone() is the best way for copying arrays? @procrastinator demonstrates that given an array, original.clone()
is in average two times faster than System.arraycopy(original, 0, destination, 0, length)
However I have noticed that when using the clone
method it is not possible to modify the length of the destination array, nor copy only a portion of the array. Using System.arraycopy
I would do:
New array with extra positions
int[] original = new int[] {1,2,3,4,5};
int originalSize = original.length;
int newSize = originalSize + 1;
int[] destination = new int[newSize];
System.arraycopy(original, 0, destination, 0)
destination[newSize - 1] = newValue;
New array with less positions
int[] original = new int[] {1,2,3,4,5};
int originalSize = original.length;
int newSize = originalSize - 1;
int[] destination = new int[newSize];
System.arraycopy(original, 1, destination, 0)
(Notice that the arrays in the example are small for the sake of clarity but in the real case they are bigger)
Is there a way to achieve a similar performance to clone
in any of the scenarios? Or in these cases we must use the System.arraycopy
method?
As suggested by @aUserHimself I have tried (without any success) to measure the performance of the System.arraycopy
against the Stream
interface. Below I provide the Benchmark code and its results:
@Benchmark
public int[] SystemArraycopySmaller() {
final int length = this.size;
int[] destination = new int[length / 2];
System.arraycopy(this.original, 0, destination, 0, length / 2);
return destination;
}
@Benchmark
public int[] StreamArraycopySmaller() {
final int length = this.size;
int[] destination = Arrays.stream(this.original).filter(i -> i < length / 2).toArray();
return destination;
}
@Benchmark
public int[] SystemArraycopyBigger() {
final int length = this.size;
int[] destination = new int[length * length];
for (int i = 0; i < length; i++) {
System.arraycopy(this.original, 0, destination, i * length, length);
}
return destination;
}
@Benchmark
public int[] StreamArraycopyBigger() {
int[] destination = Arrays.stream(this.original).flatMap(i -> Arrays.stream(this.original).map(j -> j)).toArray();
return destination;
}
Results:
Benchmark (size) Mode Cnt Score Error Units
SampleBenchmark.StreamArraycopyBigger 10000 thrpt 10 ≈ 0 ops/s
SampleBenchmark.StreamArraycopyBigger 1000 thrpt 10 ≈ 0 ops/s
SampleBenchmark.StreamArraycopyBigger 100 thrpt 10 11,997 ± 0,002 ops/s
SampleBenchmark.StreamArraycopyBigger 10 thrpt 10 608,899 ± 8,975 ops/s
SampleBenchmark.StreamArraycopyBigger 1 thrpt 10 6373,457 ± 313,626 ops/s
SampleBenchmark.StreamArraycopySmaller 10000 thrpt 10 36,692 ± 0,728 ops/s
SampleBenchmark.StreamArraycopySmaller 1000 thrpt 10 328,875 ± 2,259 ops/s
SampleBenchmark.StreamArraycopySmaller 100 thrpt 10 2141,368 ± 8,832 ops/s
SampleBenchmark.StreamArraycopySmaller 10 thrpt 10 9018,659 ± 118,933 ops/s
SampleBenchmark.StreamArraycopySmaller 1 thrpt 10 12954,709 ± 114,621 ops/s
SampleBenchmark.SystemArraycopyBigger 10000 thrpt 10 ≈ 0 ops/s
SampleBenchmark.SystemArraycopyBigger 1000 thrpt 10 ≈ 0 ops/s
SampleBenchmark.SystemArraycopyBigger 100 thrpt 10 161,004 ± 1,361 ops/s
SampleBenchmark.SystemArraycopyBigger 10 thrpt 10 10039,397 ± 123,553 ops/s
SampleBenchmark.SystemArraycopyBigger 1 thrpt 10 42539,869 ± 1965,589 ops/s
SampleBenchmark.SystemArraycopySmaller 10000 thrpt 10 399,816 ± 6,503 ops/s
SampleBenchmark.SystemArraycopySmaller 1000 thrpt 10 3189,271 ± 117,936 ops/s
SampleBenchmark.SystemArraycopySmaller 100 thrpt 10 22533,102 ± 183,870 ops/s
SampleBenchmark.SystemArraycopySmaller 10 thrpt 10 45577,443 ± 1656,788 ops/s
SampleBenchmark.SystemArraycopySmaller 1 thrpt 10 41657,519 ± 183,266 ops/s
Does anyone know about any other possible approach?
I have updated the benchmark codes and results with the suggested modifications to make the comparison even. However, for the bigger experimentation there is some error (probably due to the heap size I suppose) and for the smaller experimentation the Arraycopy
still outperforms the Stream
one. So I suppose there is no better way to copy arrays when the destination differs on size than using arraycopy
.
Upvotes: 3
Views: 2955
Reputation: 1597
You can try to measure the performance against java8
's Streams as well, it can be useful when you need to filter out or add new elements to the destination
array:
public static void copyBigArrayAndFilter() {
long time = System.currentTimeMillis();
int[] original = IntStream.range(0, 10_000).toArray();
int[] destination = Arrays.stream(original).filter(i -> i > 1_000 && i < 9_000).toArray();
System.out.println("Original size: " + original.length);
System.out.println("Destination size: " + destination.length);
System.out.println("Duration: " + (System.currentTimeMillis() - time) + " ms." );
}
public static void copyBigArrayAndAdd() {
long time = System.currentTimeMillis();
int[] original = IntStream.range(0, 10_000).toArray();
int[] destination = Arrays.stream(original).flatMap(i -> Arrays.stream(original).map(j -> i + j)).toArray();
System.out.println("Original size: " + original.length);
System.out.println("Destination size: " + destination.length);
System.out.println("Duration: " + (System.currentTimeMillis() - time) + " ms." );
}
UPDATE:
I am not an expert myself, but your question is interesting and I just had the idea of using streams
in case you have processing to do on the original
array before it is copied to destination
. (see copyBigger
examples)
For copySmaller
examples, we are doing different operations: you are copying the first half of the original
into destination
, I am copying elements that are greater than length / 2
, requiring in my case a full iteration over the original
. How would you implement this using System.arraycopy
?
For SystemArraycopyBigger
you are just setting your destination
array size to be double the original
one, but you are only copying size
in the end. Notice that in my StreamArraycopyBigger
the destination
array has size ^ 2
elements, not size * 2
: for each element in the original
array, we had an extra size
number of elements.
The results may not change by much in the end, but please try this instead if you want to test equivalent operations and not compare apples and oranges.
Also, why not try bigger sample sizes as well, like 10_000
or 1_000_000
?
@Benchmark
public int[] SystemArraycopyBigger() {
int i;
final int length = this.size;
int[] destination = new int[length * length];
for(i = 0; i < length; i++) {
System.arraycopy(this.original, 0, destination, i * length, length);
}
return destination;
}
@Benchmark
public int[] StreamArraycopyBigger() {
int[] destination = Arrays.stream(this.original).flatMap(i -> Arrays.stream(this.original).map(j -> j)).toArray();
return destination;
}
I am not sure this is what you want. But what I am trying to point out is: if you already have the array processed, there are little chances you can do better than System.arraycopy
. But if you need to modify/process and only copy a part of it, streams
might prove faster.
Upvotes: 1