¿Es mejor utilizar System.arraycopy (…) que un bucle for para copiar matrices?

Quiero crear una nueva matriz de objetos que junten dos matrices más pequeñas.

No pueden ser nulos, pero el tamaño puede ser 0.

No puedo elegir entre estas dos formas: ¿son equivalentes o son una más eficiente (por ejemplo, system.arraycopy () copia trozos enteros)?

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); 

o

 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] } } 

¿La única diferencia es el aspecto del código?

EDITAR: gracias por la pregunta vinculada, pero parecen tener una discusión sin resolver:

¿Es realmente más rápido si it is not for native types : byte [], Object [], char []? en todos los demás casos, se ejecuta una verificación de tipo, que sería mi caso y, por lo tanto, sería equivalente … ¿no?

En otra pregunta relacionada, dicen que the size matters a lot , para size> 24 system.arraycopy () gana, para menos de 10, manual for loop es mejor …

Ahora estoy realmente confundido.

 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); } 

Sé que las pruebas JUnit no son realmente las mejores para el benchmarking, pero
testHardCopyBytes tomó 0.157s para completar
y
testArrayCopyBytes tomó 0.086s para completar.

Creo que depende de la máquina virtual, pero parece que copia bloques de memoria en lugar de copiar elementos de una sola matriz. Esto boostía absolutamente el rendimiento.

EDITAR:
Parece que el rendimiento de System.arraycopy está por todas partes. Cuando se utilizan cadenas en lugar de bytes y las matrices son pequeñas (tamaño 10), obtengo estos resultados:

  String HC: 60306 ns String AC: 4812 ns byte HC: 4490 ns byte AC: 9945 ns 

Esto es lo que parece cuando las matrices tienen un tamaño de 0x1000000. Parece que System.arraycopy definitivamente gana con arreglos más grandes.

  Strs HC: 51730575 ns Strs AC: 24033154 ns Bytes HC: 28521827 ns Bytes AC: 5264961 ns 

¡Qué peculiar!

Gracias, Daren, por señalar que las referencias se copian de manera diferente. ¡Hizo esto un problema mucho más interesante!

Arrays.copyOf(T[], int) es más fácil de leer. Internaly usa System.arraycopy() que es una llamada nativa.

¡No puedes obtenerlo más rápido!

Depende de la máquina virtual, pero System.arraycopy debería proporcionarle lo más cerca posible del rendimiento nativo.

He trabajado durante 2 años como desarrollador java para sistemas integrados (donde el rendimiento es una gran prioridad) y en cualquier lugar donde se pueda usar System.arraycopy, lo he usado / visto principalmente en el código existente. Siempre se prefiere a los bucles cuando el rendimiento es un problema. Si el rendimiento no es un gran problema, me gustaría ir con el bucle, sin embargo. Mucho más fácil de leer.

La ejecución de métodos nativos como Arrays.copyOf(T[], int) tiene cierta sobrecarga, pero no significa que no sea rápida ya que la estás ejecutando usando JNI.

La forma más fácil es escribir un punto de referencia y una prueba.

Puede verificar que Arrays.copyOf(T[], int) sea ​​más rápido que su ciclo for normal.

El código de referencia desde aquí : –

 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() utiliza JNI (Java Native Interface) para copiar una matriz (o partes de ella), por lo que es increíblemente rápido, como puede confirmar aquí

En lugar de confiar en la especulación y la información posiblemente obsoleta, ejecuté algunos puntos de referencia usando el calibre . De hecho, Caliper viene con algunos ejemplos, incluido un CopyArrayBenchmark que mide exactamente esta pregunta. Todo lo que tienes que hacer es correr

 mvn exec:java -Dexec.mainClass=com.google.caliper.runner.CaliperMain -Dexec.args=examples.CopyArrayBenchmark 

Mis resultados se basan en Java HotSpot (TM) 64-Bit Server VM de Oracle, 1.8.0_31-b13, que se ejecuta en una MacBook Pro de mediados de 2010 (macOS 10.11.6 con Intel Arrandale i7, 8 GiB RAM). No creo que sea útil publicar los datos de tiempo sin procesar. Más bien, resumiré las conclusiones con las visualizaciones de apoyo.

En resumen:

  • Escribir un manual for bucle para copiar cada elemento en una matriz recién instanciada nunca es ventajoso, ya sea para arreglos cortos o arrays largos.
  • Arrays.copyOf( array , array .length) y array .clone() son ambos consistentemente rápidos. Estas dos técnicas son casi idénticas en rendimiento; cuál eliges es una cuestión de gusto.
  • System.arraycopy( src , 0, dest , 0, src .length) es casi tan rápido como Arrays.copyOf( array , array .length) y array .clone() , pero no del todo. (Consulte el caso de 50000 int s). Debido a eso y a la verbosidad de la llamada, recomendaría System.arraycopy() si necesita un control preciso sobre los elementos que se copiarán.

Aquí están los diagtwigs de tiempo:

Tiempos para copiar arreglos de longitud 5 Tiempos para copiar matrices de longitud 500 Tiempos para copiar matrices de longitud 50000

¿Cómo es posible que Arrays.copyOf sea ​​más rápido que System.arraycopy si esta es la implementación de 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; } 

System.arraycopy() es una llamada nativa que copia la operación directamente en la memoria. La única copia de memoria sería siempre más rápida que su bucle for