Is it better to use System.arraycopy(...) than a for loop for copying arrays?

Java

Java 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) and array.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 as Arrays.copyOf(array, array.length) and array.clone(), but not quite consistently so. (See the case for 50000 ints.) Because of that, and the verbosity of the call, I would recommend System.arraycopy() if you need fine control over which elements get copied where.

Here are the timing plots:

Timings for copying arrays of length 5 Timings for copying arrays of length 500 Timings for copying arrays of length 50000

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDarenView Question on Stackoverflow
Solution 1 - JavaTrent SmallView Answer on Stackoverflow
Solution 2 - JavaPhilipp SanderView Answer on Stackoverflow
Solution 3 - Javauser2742438View Answer on Stackoverflow
Solution 4 - Java200_successView Answer on Stackoverflow
Solution 5 - JavaDimaDView Answer on Stackoverflow
Solution 6 - JavaRahul TripathiView Answer on Stackoverflow
Solution 7 - JavaNandkumar TekaleView Answer on Stackoverflow