Is it better to use System.arraycopy(...) than a for loop for copying arrays?
JavaJava Problem Overview
I want to create a new array of objects putting together two smaller arrays.
They can't be null, but size may be 0.
I can't chose between these two ways: are they equivalent or is one more efficient (for example system.arraycopy() copies whole chunks)?
MyObject[] things = new MyObject[publicThings.length+privateThings.length];
System.arraycopy(publicThings, 0, things, 0, publicThings.length);
System.arraycopy(privateThings, 0, things, publicThings.length, privateThings.length);
or
MyObject[] things = new MyObject[publicThings.length+privateThings.length];
for (int i = 0; i < things.length; i++) {
if (i<publicThings.length){
things[i] = publicThings[i]
} else {
things[i] = privateThings[i-publicThings.length]
}
}
Is the only difference the look of the code?
EDIT: thanks for linked question, but they seem to have an unsolved discussion:
Is it truly faster if it is not for native types
: byte[], Object[], char[]? in all other cases, a type check is executed, which would be my case and so would be equivalent... no?
On another linked question, they say that the size matters a lot
, for size >24 system.arraycopy() wins, for smaller than 10, manual for loop is better...
Now I'm really confused.
Java Solutions
Solution 1 - Java
public void testHardCopyBytes()
{
byte[] bytes = new byte[0x5000000]; /*~83mb buffer*/
byte[] out = new byte[bytes.length];
for(int i = 0; i < out.length; i++)
{
out[i] = bytes[i];
}
}
public void testArrayCopyBytes()
{
byte[] bytes = new byte[0x5000000]; /*~83mb buffer*/
byte[] out = new byte[bytes.length];
System.arraycopy(bytes, 0, out, 0, out.length);
}
I know JUnit tests aren't really the best for benchmarking, but
testHardCopyBytes took 0.157s to complete
and
testArrayCopyBytes took 0.086s to complete.
I think it depends on the virtual machine, but it looks as if it copies blocks of memory instead of copying single array elements. This would absolutely increase performance.
EDIT:
It looks like System.arraycopy 's performance is all over the place.
When Strings are used instead of bytes, and arrays are small (size 10),
I get these results:
String HC: 60306 ns
String AC: 4812 ns
byte HC: 4490 ns
byte AC: 9945 ns
Here is what it looks like when arrays are at size 0x1000000. It looks like System.arraycopy definitely wins with larger arrays.
Strs HC: 51730575 ns
Strs AC: 24033154 ns
Bytes HC: 28521827 ns
Bytes AC: 5264961 ns
How peculiar!
Thanks, Daren, for pointing out that references copy differently. It made this a much more interesting problem!
Solution 2 - Java
Arrays.copyOf(T[], int)
is easier to read.
Internaly it uses System.arraycopy()
which is a native call.
You can't get it faster!
Solution 3 - Java
It depends on the virtual machine, but System.arraycopy should give you the closest you can get to native performance.
I've worked for 2 years as a java developer for embedded systems (where performance is a huge priority) and everywhere System.arraycopy could be used, I've mostly used it / seen it used in existing code. It's always preferred over loops when performance is an issue. If performance isn't a big issue, I'd go with the loop, though. Much easier to read.
Solution 4 - Java
Instead of relying on speculation and possibly outdated information, I ran some benchmarks using [tag:caliper]. In fact, Caliper comes with some examples, including a CopyArrayBenchmark
that measures exactly this question! All you have to do is run
mvn exec:java -Dexec.mainClass=com.google.caliper.runner.CaliperMain -Dexec.args=examples.CopyArrayBenchmark
My results are based on Oracle's Java HotSpot(TM) 64-Bit Server VM, 1.8.0_31-b13, running on a mid-2010 MacBook Pro (macOS 10.11.6 with an Intel Arrandale i7, 8 GiB RAM). I don't believe that it's useful to post the raw timing data. Rather, I'll summarize the conclusions with the supporting visualizations.
In summary:
- Writing a manual
for
loop to copy each element into a newly instantiated array is never advantageous, whether for short arrays or long arrays. Arrays.copyOf(array, array.length)
andarray.clone()
are both consistently fast. These two techniques are nearly identical in performance; which one you choose is a matter of taste.System.arraycopy(src, 0, dest, 0, src.length)
is almost as fast asArrays.copyOf(array, array.length)
andarray.clone()
, but not quite consistently so. (See the case for 50000int
s.) Because of that, and the verbosity of the call, I would recommendSystem.arraycopy()
if you need fine control over which elements get copied where.
Here are the timing plots:
Solution 5 - Java
It is not possible that Arrays.copyOf
is faster than System.arraycopy
since this is the implementation of copyOf
:
public static int[] copyOf(int[] original, int newLength) {
int[] copy = new int[newLength];
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
Solution 6 - Java
Executing native methods like Arrays.copyOf(T[], int)
does have some overhead but it doesnot mean that it is not fast as you are executing it using JNI.
The easiest way is to write a benchmark and test.
You can check that Arrays.copyOf(T[], int)
is faster than your normal for
loop.
The benchmark code from here:-
public void test(int copySize, int copyCount, int testRep) {
System.out.println("Copy size = " + copySize);
System.out.println("Copy count = " + copyCount);
System.out.println();
for (int i = testRep; i > 0; --i) {
copy(copySize, copyCount);
loop(copySize, copyCount);
}
System.out.println();
}
public void copy(int copySize, int copyCount) {
int[] src = newSrc(copySize + 1);
int[] dst = new int[copySize + 1];
long begin = System.nanoTime();
for (int count = copyCount; count > 0; --count) {
System.arraycopy(src, 1, dst, 0, copySize);
dst[copySize] = src[copySize] + 1;
System.arraycopy(dst, 0, src, 0, copySize);
src[copySize] = dst[copySize];
}
long end = System.nanoTime();
System.out.println("Arraycopy: " + (end - begin) / 1e9 + " s");
}
public void loop(int copySize, int copyCount) {
int[] src = newSrc(copySize + 1);
int[] dst = new int[copySize + 1];
long begin = System.nanoTime();
for (int count = copyCount; count > 0; --count) {
for (int i = copySize - 1; i >= 0; --i) {
dst[i] = src[i + 1];
}
dst[copySize] = src[copySize] + 1;
for (int i = copySize - 1; i >= 0; --i) {
src[i] = dst[i];
}
src[copySize] = dst[copySize];
}
long end = System.nanoTime();
System.out.println("Man. loop: " + (end - begin) / 1e9 + " s");
}
public int[] newSrc(int arraySize) {
int[] src = new int[arraySize];
for (int i = arraySize - 1; i >= 0; --i) {
src[i] = i;
}
return src;
}
System.arraycopy()
uses JNI (Java Native Interface) to copy an array (or parts of it), so it is blazingly fast, as you can confirm here
Solution 7 - Java
System.arraycopy()
is a native call which does copy operation directly at memory. Single memory copy would be always faster than your for loop