2010-06-26 11 views
5

Me han referenciado mi programa multihreaded usando -agentlib:hprof=cpu=samples y se sorprendió al encontrar la siguiente línea en los resultados:Java perfiles: java.lang.Object.hashCode toma la mitad del tiempo de CPU, pero nunca llamó explicitamente

rank self accum count trace method 
    1 52.88% 52.88% 8486 300050 java.lang.Object.hashCode 

Nunca llamo explícitamente a hashCode() en mi programa. ¿Cuál puede ser el motivo de esto? ¿Cómo puedo entender la fuente de este tiempo como "desperdicio" y si es normal o no?

Gracias, David

+1

Sería bueno si explicaras por qué esto es un problema en primer lugar. –

Respuesta

5

Lo más probable es que esté utilizando de forma intensiva un mapa como un HashMap.

HashMap usó el hashCode para distribuir los objetos. Si está utilizando muchos objetos con esta estructura de datos, es muy importante que su .equals y su método .hashCode estén implementados correctamente.

Ver: A partir del artículo 8 de Java: Always override hashCode when you override equals

+0

Gracias! Probablemente no tengas razón. En realidad, puedo renunciar a mi uso de las capacidades de acceso aleatorio (¿así es como lo llamas?), Y no me importa el orden de los objetos. Solo necesito poder agregar objetos y luego iterar sobre todos ellos. Además, este es un conjunto (no necesito el mismo objeto más de una vez), pero tampoco intentaré agregarlo más de una vez ... Debo usar una lista (aunque no me importa) sobre el pedido)? ¿Cuál es la estructura de datos más eficiente para dicho conjunto? –

+0

También podría ser un HashSet ... –

+0

Es de hecho un HashSet. Ahora convertí todo a ArrayLists. Ahora esta línea desaparece del perfilador, pero el tiempo de ejecución total se mantuvo muy similar. Extraño. ¿No es así? –

0

Está mot probablemente a la derecha. De hecho, puedo renunciar a mi uso de las capacidades de acceso aleatorio (¿así es como lo llamas?), Y no me importa el orden de los objetos. Solo necesito poder agregar objetos y luego iterar sobre todos ellos. Además, este es un conjunto (no necesito el mismo objeto más de una vez), pero tampoco intentaré agregarlo más de una vez ... ¿Debería usar una lista (aunque no me importa? el pedido)? ¿Cuál es la estructura de datos más eficiente para dicho conjunto?

Un HashSet se implementa como un HashMap que asigna la clave a sí mismo, por lo que cambiar a un HashSet no tendrá mucha importancia, en cuanto al rendimiento.

Las otras alternativas son un TreeSet, o (suponiendo que su aplicación nunca intente insertar un duplicado) una de las clases List. Si su aplicación es tal que una Lista funcionará, entonces ArrayList o LinkedList serán más eficientes que HashSet o TreeSet.

Sin embargo, hay algo muy desagradable en su aplicación que pasa el 50% de su tiempo en los métodos hashCode. A menos que las tablas hash se redimensionen, el método hashCode solo se debe llamar una vez por conjunto o por operación de mapa. Entonces, o bien hay un montón de cambio de tamaño de mapa/conjunto, o está realizando un gran número de operaciones de conjunto add. (Que yo sepa, el método de código hash del objeto es barato, por lo que el coste de cada llamada no debería ser un problema.)

EDITAR

Está nextInt() muy caro? Alguna alternativa?

No, no es caro. Eche un vistazo al código. La clase Random (y el método nextInt()) hace uso de un AtomicLong para que sea seguro para subprocesos, y puede guardar algunos ciclos si codificó una versión no segura para subprocesos. El código fuente está en su directorio de instalación JDK ... eche un vistazo.

+0

De hecho, muchos agregan operaciones. Como mencioné, cambié a ArrayList pero el tiempo total simplemente mejoró ligeramente. Ahora, el mejor consumidor de tiempo de CPU es: rango recuento de auto acumular método de rastreo 1 19.30% 19.30% 2727 300122 java.util.Random.next De hecho, llamo rand.nextInt() muchas veces para obtener enteros entre 0 y ~ 10^7. ¿Es nextInt() realmente caro? Alguna alternativa? –

1

Una cosa que debe hacer es comprobar hacia fuera hacer juego seguimiento de la pila para ver quién está llamando; los cambios son, de hecho, es HashMap.

Pero más allá de esto, he notado que hprof tiende a sobreestimar las llamadas a hashCode(); y realmente me gustaría saber cómo y por qué. Esto se basa en conocer el perfil de desempeño aproximado del código; y he visto un 50% de uso de la CPU (por muestreo), donde es casi seguro que no demorará tanto. La implementación de hashCode() simplemente devuelve un campo int, y el método es final (en el objeto final). Así que es básicamente un artefacto de perfil de algún tipo ... simplemente no tengo idea de cómo o por qué, o cómo deshacerse de él.

Cuestiones relacionadas