Recientemente, noté que declarar una matriz que contiene 64 elementos es mucho más rápido (> 1000 veces) que declarar el mismo tipo de matriz con 65 elementos.
Aquí está el código que usé para probar esto:
public class Tests{
public static void main(String args[]){
double start = System.nanoTime();
int job = 100000000;//100 million
for(int i = 0; i < job; i++){
double[] test = new double[64];
}
double end = System.nanoTime();
System.out.println("Total runtime = " + (end-start)/1000000 + " ms");
}
}
Esto se ejecuta en aproximadamente 6 ms, si reemplazo new double[64]
con new double[65]
que tarda aproximadamente 7 segundos. Este problema se vuelve exponencialmente más grave si el trabajo se distribuye en más y más subprocesos, que es donde se origina mi problema.
Este problema también ocurre con diferentes tipos de matrices como int[65]
o String[65]
. Este problema no ocurre con cadenas grandes:, String test = "many characters";
pero comienza a ocurrir cuando se cambia aString test = i + "";
Me preguntaba por qué es así y si es posible eludir este problema.
System.nanoTime()
debería preferirseSystem.currentTimeMillis()
a la evaluación comparativa.byte
lugar dedouble
.Respuestas:
Está observando un comportamiento causado por las optimizaciones realizadas por el compilador JIT de su máquina virtual Java. Este comportamiento se puede reproducir con matrices escalares de hasta 64 elementos y no se activa con matrices de más de 64.
Antes de entrar en detalles, echemos un vistazo más de cerca al cuerpo del bucle:
El cuerpo no tiene ningún efecto (comportamiento observable) . Eso significa que no hay diferencia fuera de la ejecución del programa si esta declaración se ejecuta o no. Lo mismo es cierto para todo el ciclo. Por lo tanto, podría suceder que el optimizador de código traduzca el bucle en algo (o nada) con el mismo comportamiento funcional y de tiempo diferente.
Para los puntos de referencia, al menos debe cumplir con las siguientes dos pautas. Si lo hubiera hecho, la diferencia habría sido significativamente menor.
Ahora entremos en detalles. No es sorprendente que exista una optimización que se active para matrices escalares que no superen los 64 elementos. La optimización es parte del análisis de Escape . Coloca objetos pequeños y arreglos pequeños en la pila en lugar de asignarlos al montón, o incluso mejor, optimizarlos por completo. Puedes encontrar algo de información al respecto en el siguiente artículo de Brian Goetz escrito en 2005:
La optimización se puede desactivar con la opción de línea de comando
-XX:-DoEscapeAnalysis
. El valor mágico 64 para matrices escalares también se puede cambiar en la línea de comandos. Si ejecuta su programa de la siguiente manera, no habrá diferencia entre matrices con 64 y 65 elementos:Habiendo dicho eso, desaconsejo enfáticamente el uso de tales opciones de línea de comando. Dudo que haga una gran diferencia en una aplicación realista. Solo lo usaría si estuviera absolutamente convencido de la necesidad, y no basándome en los resultados de algunos pseudo benchmarks.
fuente
Hay muchas formas en las que puede haber una diferencia, según el tamaño de un objeto.
Como dijo Nosid, el JITC puede estar (muy probablemente) asignando pequeños objetos "locales" en la pila, y el tamaño de corte para las matrices "pequeñas" puede ser de 64 elementos.
La asignación en la pila es significativamente más rápida que la asignación en el montón y, más concretamente, la pila no necesita ser recolectada como basura, por lo que la sobrecarga de GC se reduce considerablemente. (Y para este caso de prueba, la sobrecarga de GC es probablemente del 80 al 90% del tiempo total de ejecución).
Además, una vez que se asigna el valor a la pila, el JITC puede realizar una "eliminación de código muerto", determinar que el resultado del
new
nunca se utiliza en ningún lugar y, después de asegurarse de que no hay efectos secundarios que se perderían, eliminar toda lanew
operación. y luego el bucle (ahora vacío) en sí.Incluso si el JITC no realiza la asignación de pila, es completamente posible que los objetos más pequeños que un cierto tamaño se asignen en un montón de manera diferente (por ejemplo, desde un "espacio" diferente) que los objetos más grandes. (Sin embargo, normalmente esto no produciría diferencias de tiempo tan dramáticas).
fuente