2010-02-21 14 views
20

¿Por qué es el segundo código más rápido?Autoboxing versus boxeo manual en Java

Map<Integer, Double> map = new HashMap<Integer, Double>(); 
for (int i = 0; i < 50000; i++) { 
    for (double j = 0.0; j < 10000; j++) { 
     map.put(i, j); 
    } 
} 

Map<Integer, Double> map=new HashMap<Integer, Double>(); 
for (int i = 0; i < 50000; i++) { 
    for (double j = 0.0; j < 10000; j++) {    
     map.put(new Integer(i), new Double(j)); 
    } 
} 
+0

Trate de envolver las dos partes y microbenchmark de nuevo. – BalusC

+2

porque fuera de tu micro-benchmark ideado que es incorrecto, no importa ya que esto se optimizará pero eventualmente JIT en el mundo real. –

Respuesta

46

Autoboxing utiliza Integer.valueOf, que guarda en caché internamente objetos enteros para enteros pequeños (por defecto -128 a 127, pero el valor máximo se puede configurar con la propiedad "java.lang.Integer.IntegerCache.high" - ver el código fuente de Integer.valueOf), por lo que es diferente de llamar directamente al new Integer. Como Integer.valueOf realiza una comprobación rápida de la magnitud del valor entero antes de llamar al new Integer, es un poco más rápido llamar al new Integer directamente (aunque usa más memoria si tiene muchos enteros pequeños). La asignación en Java es muy rápida, y el tiempo de GC es proporcional al número de objetos vivos de corta duración (es decir, no es proporcional a la cantidad de basura), por lo que el GC también es muy rápido.

Pero según la versión de JVM y qué optimizaciones están habilitadas, existe la optimización de reemplazo escalar, que puede producir una diferencia de rendimiento mucho mayor cuando se asignan objetos de corta vida (en su ejemplo no se puede optimizar, porque está almacenando los objetos en un mapa, pero en muchas otras situaciones es útil).

En versiones JVM recientes hay optimización scalar replacement (excepto en 1.6.0_18 donde el análisis de escape es temporarily disabled), lo que significa que las asignaciones de objetos efímeros pueden optimizarse. Cuando el reemplazo escalar en JVM era nuevo, alguien hizo a benchmark donde había un código similar al suyo. El resultado fue que el código que utilizaba primitivas era el más rápido, el código con llamadas explícitas new Integer() era casi tan rápido como el que usaba primitivas, y el código que utilizaba el autoboxing era mucho más lento. Esto se debió a que el autoboxing usa Integer.valueOf y al menos entonces la optimización de reemplazo escalar no tomó en consideración ese caso especial. No sé si la optimización se ha mejorado desde entonces.

+1

"Porque Integer.valueOf hace una comprobación rápida del tamaño del número entero antes de llamar al nuevo entero". Un entero Java es siempre del mismo tamaño. –

+4

Quiere decir que verifica el valor int que se le está pasando para ver si está dentro del rango de valores que están en la memoria caché. – ColinD

+1

El valor y el tamaño tienen diferentes significados. –

7

¿Porque los resultados de microbenchmarks no son confiables?

Además, el auto-boxeo se hace usando Integer.valueOf() y Double.valueOf(), no los constructores.

14

Autoboxing utilizará Integer.valueOf y Double.valueOf. Hay una sobrecarga en llamar a esos métodos (aunque eventualmente se inline). También Integer.valueOf realiza algunas comprobaciones de valores bajos para usar instancias agrupadas, lo que no suele ser una ganancia en el código (aunque podría reducir un poco el tamaño del almacenamiento dinámico). Las instancias combinadas pueden ser una ganancia donde reducen el tamaño del almacenamiento dinámico, los tiempos de GC e incluso pueden mejorar el rendimiento de la prueba de igualdad.

Pero, en general, es una microoptimización que, en general, debe ignorar.