2010-06-26 31 views
5

Ho il mio programma di benchmark multihreaded utilizzando -agentlib:hprof=cpu=samples e sono rimasto sorpreso di trovare la seguente riga nei risultati:Java profiling: java.lang.Object.hashCode prende la metà del tempo di CPU, ma mai esplicitamente chiamato

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

ho mai chiamo esplicitamente hashCode() nel mio programma. Quale può essere la ragione di questo? Come posso capire la fonte per questo "spreco" di tempo e se è normale o no?

Grazie, David

+1

Sarebbe bello se dovessi spiegare perché questo è un problema in primo luogo. –

risposta

5

Molto probabilmente si sta utilizzando molto intensamente una mappa, ad esempio un HashMap.

HashMap utilizzarlo hashCode per distribuire gli oggetti. Se stai usando molti oggetti con questa struttura dati, è molto importante il vostro .equals e il metodo di .hashCode siano attuate correttamente.

See: Effective Java Articolo 8: Always override hashCode when you override equals

+0

Grazie! Probabilmente hai ragione. Posso davvero rinunciare al mio uso delle capacità di accesso casuale (è così che lo chiamate?), E non mi interessa l'ordine degli oggetti. Devo solo essere in grado di aggiungere oggetti, quindi iterare su tutti loro. Inoltre, questo è davvero un set (non ho bisogno dello stesso oggetto più di una volta), ma non tenterò mai di aggiungerlo più di una volta ... Dovrei usare una lista invece (anche se non mi interessa sull'ordinazione)? Qual è la struttura dati più efficiente per un set di questo tipo? –

+0

Potrebbe anche essere un HashSet ... –

+0

In effetti è un HashSet. Ora ho convertito tutto in ArrayLists. Ora questa linea scompare dal profiler ma il tempo di esecuzione totale è rimasto molto simile. Strano. non è vero ?! –

0

Sei mot probabilmente ragione. Posso davvero rinunciare al mio uso delle capacità di accesso casuale (è così che lo chiamate?), E non mi interessa l'ordine degli oggetti. Devo solo essere in grado di aggiungere oggetti, quindi iterare su tutti loro. Inoltre, questo è davvero un set (non ho bisogno dello stesso oggetto più di una volta), ma non tenterò mai di aggiungerlo più di una volta ... Dovrei usare una lista invece (anche se non mi interessa l'ordine)? Qual è la struttura dati più efficiente per un set di questo tipo?

Un HashSet è implementato come un HashMap che mappa la chiave stessa, per cui il passaggio di un HashSet non farà molta differenza, prestazioni-saggio.

Le altre alternative sono una TreeSet, o (supponendo che l'applicazione non potrà mai provare ad inserire un duplicato) una delle classi List. Se la tua applicazione è tale da far funzionare un elenco, una ArrayList o LinkedList sarà più efficiente di un HashSet o TreeSet.

Tuttavia, c'è qualcosa di molto sospetto circa la vostra applicazione di spesa del 50% del suo tempo in hashCode metodi. A meno che le tabelle hash non vengano ridimensionate, il metodo hashCode dovrebbe essere chiamato una sola volta per operazione set o map. Quindi, c'è un sacco di ridimensionamento della mappa/set in corso, o stai facendo un numero enorme di operazioni di add impostate. (AFAIK, il metodo oggetto codice hash è a buon mercato, in modo che il costo di ogni chiamata non dovrebbe essere un problema.)

EDIT

Is nextInt() molto costoso? Qualche alternativa?

No, non è costoso. Dai un'occhiata al codice. La classe Random (e il metodo nextInt()) fa uso di un AtomicLong per renderlo thread-safe, e potresti salvare alcuni cicli se hai codificato una versione non thread-safe. Il codice sorgente è nella directory di installazione JDK ... dai un'occhiata.

+0

In effetti, molte aggiungono operazioni. Come accennato, sono passato a ArrayList ma il tempo totale è leggermente migliorato. Ora, il tempo di utilizzo della CPU più elevato è il seguente: rank self accum count metodo 1 19,30% 19,30% 2727 300122 java.util.Random.next Io in effetti chiamo rand.nextInt() molte volte per ottenere numeri interi tra 0 e ~ 10^7. NextInt() è davvero costoso? Qualche alternativa? –

1

Una cosa che si dovrebbe fare è quello di controllare corrispondenza traccia dello stack per vedere chi sta chiamando; i cambiamenti sono infatti HashMap.

Ma al di là di questo, ho notato che hprof tende a sovrastimare le chiamate a hashCode(); e mi piacerebbe davvero sapere come e perché. Questo è basato sul reale conoscere il profilo di prestazione approssimativo del codice; e ho visto il 50% percento di utilizzo della CPU (tramite campionamento), dove è tutto ma certo che non ci vorrà molto. L'implementazione di hashCode() restituisce solo un campo int e il metodo è finale (sull'oggetto finale). Quindi è fondamentalmente un artefatto di profiler di qualche tipo ... non ha idea di come o perché, o come liberarsene.