2010-04-09 18 views
8

que tienen los dos programas siguientes:velocidad de iteración de int vs largo

long startTime = System.currentTimeMillis(); 
for (int i = 0; i < N; i++); 
long endTime = System.currentTimeMillis(); 
System.out.println("Elapsed time: " + (endTime - startTime) + " msecs"); 

y

long startTime = System.currentTimeMillis(); 
for (long i = 0; i < N; i++); 
long endTime = System.currentTimeMillis(); 
System.out.println("Elapsed time: " + (endTime - startTime) + " msecs"); 

Nota: la única diferencia es el tipo de la variable de bucle (int y long).

Cuando ejecuto esto, el primer programa imprime constantemente entre 0 y 16 mseg, independientemente del valor de N. El segundo toma mucho más tiempo. Para N == Integer.MAX_VALUE, se ejecuta en aproximadamente 1800 mseg en mi máquina. El tiempo de ejecución parece ser más o menos lineal en N.

¿Por qué es esto?

Supongo que el compilador JIT optimiza el ciclo int hasta la muerte. Y por una buena razón, porque obviamente no hace nada. ¿Pero por qué no lo hace para el lazo long también?

Un colega pensó que podríamos estar midiendo el compilador JIT haciendo su trabajo en el ciclo long, pero dado que el tiempo de ejecución parece lineal en N, probablemente este no sea el caso.

estoy usando JDK 1.6.0 actualización 17:

C:\>java -version 
java version "1.6.0_17" 
Java(TM) SE Runtime Environment (build 1.6.0_17-b04) 
Java HotSpot(TM) 64-Bit Server VM (build 14.3-b01, mixed mode) 

estoy en Windows XP Professional x64 Edition, Service Pack 2, con una CPU Intel Core2 Quad a 2,40 GHz.


RENUNCIA

sé que microbenchmarks no son útiles en la producción. También sé que System.currentTimeMillis() no es tan preciso como sugiere su nombre. Esto es solo algo que noté mientras bromeaba, y simplemente tenía curiosidad sobre por qué sucede esto; nada mas.

+0

absolutamente Estoy de acuerdo con @Andrzej aquí, pero si en realidad es sólo por curiosidad, podría usar el complemento PrintAssembly para ver realmente el código generado: http://wikis.sun.com/display/HotSpotInternals/PrintAssembly –

+0

¿Qué sucede si se ejecuta con el indicador -Xint? Esto evita la compilación de zonas activas, por lo que obtendrá una mejor comparación. –

+0

@Steven: pero con '-Xint' el resultado es * aún menos * significativo para cualquier cosa que se parezca al uso real. –

Respuesta

5

Es una pregunta interesante, pero para ser sincero, no estoy convencido de que teniendo en cuenta el comportamiento de Hotspot aquí se obtenga información útil. Las respuestas que obtenga no serán transferibles en un caso general (porque estamos viendo las optimizaciones que realiza Hotspot en una situación específica), por lo que lo ayudarán a comprender por qué una no operación es más rápida que otra. pero no lo ayudarán a escribir programas "reales" más rápidos.

También es increíblemente fácil escribir referencias micro muy engañosas sobre este tipo de cosas; consulte this IBM DW article para conocer algunos de los errores más comunes, cómo evitarlos y algunos comentarios generales sobre lo que está haciendo.

Así que realmente esta es una respuesta "sin comentarios", pero creo que esa es la única respuesta válida.Un ciclo no operativo trivial en tiempo de compilación no necesita como para ser rápido, por lo que el compilador no está optimizado para ser rápido en algunas de estas condiciones.

+0

Tienes razón, por supuesto. La razón por la que estoy preguntando es pura curiosidad sobre algo que noté mientras bromeaba; No tengo la intención de hacer esto en un programa real. :) – jqno

4

Probablemente esté utilizando una JVM de 32 bits. Los resultados probablemente serán diferentes con una JVM de 64 bits. En una JVM de 32 bits, una int puede correlacionarse con un entero nativo de 32 bits y aumentarse con una sola operación. Lo mismo no se sostiene por mucho tiempo, lo que requerirá más operaciones para incrementar.

Consulte este question para una discusión sobre tamaños int y long.

+0

Buen punto. Sin embargo, estoy usando una JVM de 64 bits. Actualizaré la pregunta. – jqno

+0

También será útil publicar el sistema operativo y algunos detalles sobre la configuración del hardware (principalmente la CPU). – kgiannakakis

3

Mi conjetura - y es sólo una conjetura - es la siguiente:

La JVM concluye que el primer bucle efectivamente hace nada, por lo que elimina por completo. Ninguna variable "escapa" del for-loop.

En el segundo caso, el bucle tampoco hace nada. Pero podría ser que el código JVM que determina que el ciclo no hace nada tenga una cláusula "if (type of i) == int". En cuyo caso, la optimización para eliminar el bucle for-nothing solo funciona con int.

Una optimización que elimina el código tiene que estar seguro de que no hay efectos secundarios. Los codificadores de JVM parecen haberse equivocado por el lado de la precaución.

1

La micro-evaluación comparativa de este tipo no tiene mucho sentido, ya que los resultados dependen en gran medida del funcionamiento interno del HIT de zona activa.

Además, tenga en cuenta que los valores de reloj del sistema que obtiene con System.currentTimeMillis() no tienen una resolución de 1 ms en todos los sistemas operativos. No puede usar esto para cronometrar con mucha precisión eventos de muy corta duración.

Tener un vistazo a este artículo, que explica por qué hacer micro-puntos de referencia en Java no es tan fácil como la mayoría de la gente piensa: Anatomy of a flawed microbenchmark