2010-05-05 20 views
74

Me sorprendió ver en la fuente de Java que System.arraycopy es un método nativo.¿Por qué System.arraycopy es nativo en Java?

Por supuesto, la razón es porque es más rápido. Pero, ¿qué trucos nativos puede usar el código para hacerlo más rápido?

¿Por qué no simplemente recorre la matriz original y copia cada puntero a la nueva matriz, seguramente esto no es tan lento y engorroso?

Respuesta

70

en código nativo, que se puede hacer con una sola memcpy/memmove, en contraposición a n operaciones de copia distintos. La diferencia en el rendimiento es sustancial.

+0

@Peter, por lo que dentro de código nativo que puede encontrar con el modelo de memoria de Java? (Nunca he tenido ninguna causa para hacer una malarkey nativa) –

+0

@James B, no soy un experto en esto, pero en el código nativo seguramente no estás obligado con el modelo de memoria Java ni nada, puedes jugar con la cruda bits de cualquier manera que desee (bajo su propio riesgo, por supuesto). –

+7

En realidad, solo algunas subcategorías de 'arraycopy' podrían implementarse usando' memcpy'/'memmove'. Otros requieren una verificación del tipo de tiempo de ejecución para cada elemento copiado. –

14

No se puede escribir en Java. El código nativo puede ignorar o eludir la diferencia entre las matrices de Objeto y las matrices de las primitivas. Java no puede hacer eso, al menos no de manera eficiente.

Y no se puede escribir con un solo memcpy(), debido a la semántica requerida por la superposición de matrices.

+2

Bien, entonces 'memmove' entonces. Aunque no creo que haga mucha diferencia en el contexto de esta pregunta. –

+0

No memmove() tampoco, vea los comentarios de @Stephen C en otra respuesta. – EJP

+0

Vi eso ya, ya que resultó ser mi propia respuesta ;-) Pero gracias de todos modos. –

3

Hay algunas razones:

  1. El JIT es poco probable que genere código de bajo nivel tan eficiente como un código C escrito manualmente. Usar bajo nivel C puede permitir muchas optimizaciones que son casi imposibles de hacer para un compilador JIT genérico.

    Vea este enlace para algunos trucos y comparaciones de velocidad de las implementaciones de C escritas a mano (establecimiento de memoria, pero el principio es el mismo): Marque esta Optimizing Memcpy improves speed

  2. La versión C es prácticamente independiente del tipo y tamaño de los miembros de la matriz. No es posible hacer lo mismo en Java ya que no hay forma de obtener los contenidos de la matriz como un bloque de memoria sin formato (por ejemplo, un puntero).

+1

El código de Java puede optimizarse. De hecho, lo que realmente ocurre es que se genera un código de máquina que es más eficiente que el C. –

+0

Acepto que a veces el código JITed estará mejor optimizado localmente ya que sabe en qué procesador está ejecutándose. Sin embargo, dado que es "justo a tiempo", nunca podrá usar todas las optimizaciones no locales que tarden más en ejecutarse. Además, nunca podrá hacer coincidir el código C hecho a mano (que también podría tener en cuenta el procesador y anular parcialmente las ventajas de JIT, compilando para un procesador específico o mediante algún tipo de control de tiempo de ejecución). –

+1

Creo que el equipo del compilador de Sun JIT discutiría muchos de esos puntos. Por ejemplo, creo que HotSpot realiza una optimización global para eliminar el envío innecesario de métodos, y no hay ninguna razón por la cual un JIT no pueda generar código específico del procesador. Luego está el punto de que un compilador JIT puede hacer una optimización de la rama en función del comportamiento de ejecución de la aplicación actual. –

9

Por supuesto, depende de la implementación.

HotSpot lo tratará como un código "intrínseco" e insertado en el sitio de la llamada. Eso es código de máquina, no código de C viejo lento. Esto también significa que los problemas con la firma del método desaparecen en gran parte.

Un simple bucle de copia es lo suficientemente simple como para aplicarle optimizaciones obvias. Por ejemplo, desenrollar bucles. Exactamente lo que sucede depende nuevamente de la implementación.

+1

esta es una respuesta muy decente :), esp. la mención de los intrínsecos. sin ellos, la iteración simple podría ser más rápida ya que generalmente se desenrolla de todos modos con el JIT – bestsss

4

En mis propias pruebas System.arraycopy() para copiar varias matrices de dimensión es 10 a 20 veces más rápido que el entrelazado de bucles:

float[][] foo = mLoadMillionsOfPoints(); // result is a float[1200000][9] 
float[][] fooCpy = new float[foo.length][foo[0].length]; 
long lTime = System.currentTimeMillis(); 
System.arraycopy(foo, 0, fooCpy, 0, foo.length); 
System.out.println("native duration: " + (System.currentTimeMillis() - lTime) + " ms"); 
lTime = System.currentTimeMillis(); 

for (int i = 0; i < foo.length; i++) 
{ 
    for (int j = 0; j < foo[0].length; j++) 
    { 
     fooCpy[i][j] = foo[i][j]; 
    } 
} 
System.out.println("System.arraycopy() duration: " + (System.currentTimeMillis() - lTime) + " ms"); 
for (int i = 0; i < foo.length; i++) 
{ 
    for (int j = 0; j < foo[0].length; j++) 
    { 
     if (fooCpy[i][j] != foo[i][j]) 
     { 
      System.err.println("ERROR at " + i + ", " + j); 
     } 
    } 
} 

Esta impresora:

System.arraycopy() duration: 1 ms 
loop duration: 16 ms 
+8

Aunque esta pregunta es antigua, solo para el registro: NO es un punto de referencia justo (y mucho menos la pregunta si tal punto de referencia tendría sentido en el primer lugar). 'System.arraycopy' hace una copia superficial (solo se copian las * referencias * a la' float [] 'interior), mientras que sus' for' -loops anidados realizan una copia profunda ('float' por' float'). Un cambio a 'fooCpy [i] [j]' se reflejará en 'foo' usando' System.arraycopy', pero no usará los 'for'-loops anidados. – misberner

Cuestiones relacionadas