Pregunta ¿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.


76
2017-09-05 14:15


origen


Respuestas:


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 aumentaría absolutamente el rendimiento.

EDITAR:
Parece que el rendimiento de System.arraycopy está por todas partes. Cuando se usan 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!


70
2017-09-05 14:28



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!


30
2017-09-05 14:19



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.


13
2017-09-05 14:18



Ejecutando métodos nativos como Arrays.copyOf(T[], int) tiene algunos gastos generales, pero no significa que no sea rápido ya que lo está ejecutando utilizando JNI.

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

Puedes verificar eso Arrays.copyOf(T[], int) es más rápido que tu normal for lazo.

El código de referencia de 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, ya que puede confirmar aquí


6
2017-09-05 14:27



En lugar de confiar en la especulación y la información posiblemente obsoleta, ejecuté algunos puntos de referencia utilizando . De hecho, Caliper viene con algunos ejemplos, incluido un CopyArrayBenchmark eso 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 el bucle para copiar cada elemento en una matriz recién instanciada nunca es ventajoso, ya sea para arreglos cortos o arrays largos.
  • Arrays.copyOf(formación, formación.length) y formación.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(formación, formación.length) y formación.clone(), pero no tan consistentemente. (Véase el caso de 50000 ints.) Por eso, y la verbosidad de la llamada, recomendaría System.arraycopy() si necesita un control preciso sobre qué elementos se copian dónde.

Aquí están los diagramas de tiempo:

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


4
2018-03-14 04:36



¿Cómo es posible que Arrays.copyOf es más rápido entonces 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;
}

4
2018-05-10 11:37



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


3
2017-09-05 14:21