Why Is System.Arraycopy Native in Java

Why is System.arraycopy native in Java?

In native code, it can be done with a single memcpy / memmove, as opposed to n distinct copy operations. The difference in performance is substantial.

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

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!

Is there any reason to prefer System.arraycopy() over clone()?

No. If you're really microbenchmarking, then maybe, depending on what JVM you're running. But in actuality, no.

Is Java's System.arraycopy() efficient for small arrays?

Expanding a little on what Sid has written, it's very likely that System.arraycopy is just a JIT intrinsic; meaning that when code calls System.arraycopy, it will most probably be calling a JIT-specific implementation (once the JIT tags System.arraycopy as being "hot") that is not executed through the JNI interface, so it doesn't incur the normal overhead of native methods.

In general, executing native methods does have some overhead (going through the JNI interface, also some internal JVM operations cannot happen when native methods are being executed). But it's not because a method is marked as "native" that you're actually executing it using JNI. The JIT can do some crazy things.

Easiest way to check is, as has been suggested, writing a small benchmark, being careful with the normal caveats of Java microbenchmarks (warm up the code first, avoid code with no side-effects since the JIT just optimizes it as a no-op, etc).

System.arrayCopy is slow

Actually, HotSpot compiler is smart enough to unroll and vectorize manual copy loop - that's why the result code appears to be well optimized.

Why is System.arraycopy slower then? It is originally a native method, and you have to pay for a native call until the compiler optimizes it as JVM intrinsic.

However, in your test the compiler does not have a chance for such optimization, because enlarge method is not called many enough times (i.e. it is not considered as hot).

I'll show you a funny trick to force the optimization. Rewrite enlarge 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, 0, newArray, 0, array.length);
return newArray;
}

An empty loop triggers a backedge counter overflow, which in turn triggers the compilation of enlarge method. The empty loop is then eliminated from the compiled code, so it is harmless. Now enlarge method gets about 1.5x faster than the manual loop!

It is important that System.arraycopy immediately follows new int[]. In this case HotSpot can optimize away the redundant zeroing of newly allocated array. You know, all Java objects must be zeroed right after creation. But as far as compiler detects that the array is filled right after creation, it may eliminate zeroing, thus making the result code yet faster.

P.S. @assylias' benchmark is good, but it also suffers from the fact that System.arraycopy is not intrinsified for the large arrays. In case of small arrays arrayCopy benchmark is called many times per second, JIT considers it hot and optimizes well. But for large arrays each iteration is longer, so there is a lot less iterations per second, and JIT does not treat arrayCopy as hot.

Exception with System.arraycopy in Java

If you take a look at the API for System.arraycopy, you'll see that it's a method that copies data from one array to another array.

When you invoke it in your push method, you're handing it an instance of E as the target array.

However, in your Test class you've created a GenericStack<String>. Since a String is not a type of array, when you attempt to copy data into it you get an error.

What you really should be doing in your push method is first checking to see if you have more room in your existing array. If so, just add the new element and increment the size.

If your backing array is full, create a new (larger) array, copy the current backing array into it, and then add the element to the new array.

When and why should I use System.arraycopy (or: arrays vs collections)

Well, for one thing System.arraycopy(...) works on arrays, and Collection.addAll() on collections - so what you can use depends on what kind of objects you have to work with.

The alternative to System.arraycopy(...) with arrays is writing a loop and copying elements manually. This is

  • More code
  • Thus, more likely to contain an error
  • In most cases slower because System.arraycopy(...) is a native method that can use fast memory copy routines provided by the OS.

NullPointer Exception java arrays

You pass array elements and not the array to System.arraycopy. The NullPointerException is because copy[i] is null.

The right way is to pass the arrays, no need to loop over the elements:

public static PhysicsVector[] copyArray(PhysicsVector[] a) {
int length = a.length;
PhysicsVector[] copy = new PhysicsVector[length];
System.arraycopy(a, 0, copy, 0, length);
return copy;
}

In case you want a shallow copy of an array it is even easier:

public static PhysicsVector[] copyArray(PhysicsVector[] a) {
return a.clone();
}


Related Topics



Leave a reply



Submit