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
Import Sun.Misc.Base64Encoder Results in Error Compiled in Eclipse
How to Compile Multiple Java Source Files in Command Line
Difference Between Static and Final
How to Convert Double[] to Double[]
JSON and Java - Circular Reference
Passing Parameters to a Jdbc Preparedstatement
How to Prevent Eclipse from Hanging on Startup
Java Code for Getting Current Time
Threads Configuration Based on No. of Cpu-Cores
Java Generic Class - Determine Type
Fastest Way to Iterate an Array in Java: Loop Variable VS Enhanced for Statement
Use Cases for Rxjava Schedulers