Mentre lavoravo su un benchmark di memoria di alcune strutture di dati ad alto throughput, mi sono reso conto che potevo usare uno ImmutableMap
con solo un piccolo refactoring.Guava ImmutableMap ha un accesso notevolmente più lento di HashMap
Pensando che questo sarebbe stato un miglioramento, l'ho inserito nel mix e sono stato sorpreso di scoprire che non solo era più lento di HashMap
, in un ambiente a thread singolo sembra essere sempre più lento anche di ConcurrentHashMap
!
si può vedere il punto di riferimento completo qui: https://bitbucket.org/snippets/dimo414/89K7G
La carne del test è piuttosto semplice, quanto tempo ci vuole per ottenere un gran numero di stringhe casuali che possono esistere nella mappa.
public static void timeAccess(Map<String,String> map) {
Random rnd = new Random(seed);
int foundCount = 0;
long start = System.nanoTime();
for(int i = 0; i < loop; i++) {
String s = map.get(RndString.build(rnd));
if(s != null)
foundCount++;
}
long stop = System.nanoTime() - start;
System.out.println("Found "+foundCount+" strings out of "+loop+" attempts - "+
String.format("%.2f",100.0*foundCount/loop)+" success rate.");
System.out.println(map.getClass().getSimpleName()+" took "+
String.format("%.4f", stop/1_000_000_000.0)+" seconds.");
System.out.println();
}
E l'esecuzione di questo contro un HashMap
, un ConcurrentHashMap
, e un ImmutableMap
, tutti contenenti gli stessi valori, costantemente ha mostrato un rallentamento drammatico quando si utilizza ImmutableMap
- spesso verso l'alto di 15% più lento. Più sparsa è la mappa (ad esempio, più spesso è map.get()
restituita nulla) maggiore è la disparità. Ecco il risultato di un'esecuzione di esempio:
Found 35312152 strings out of 100000000 attempts - 35.31 success rate.
HashMap took 29.4538 seconds.
Found 35312152 strings out of 100000000 attempts - 35.31 success rate.
ConcurrentHashMap took 32.1465 seconds.
Found 35312152 strings out of 100000000 attempts - 35.31 success rate.
RegularImmutableMap took 37.9709 seconds.
Si tratta di un problema documentato/previsto? Il Guava Docs indica che Immutable***
è più efficiente in termini di memoria, ma non dice nulla sulla velocità. Per rallentamenti di questa portata, sono propenso ad affrontare i costi della memoria ed evitare lo Immutable***
quando la velocità è un problema (e quando no ?!). Mi sto perdendo qualcosa?
Consulta anche: https://groups.google.com/forum/?fromgroups=#!topic/guava-discuss/I7yPpa5Hlpg
I problemi menzionati in questo thread di mailing list si applicano definitivamente ai vostri benchmark. Inoltre, vedere Javadoc "ImmutableMap':" a differenza di HashMap, ImmutableMap non è ottimizzato per i tipi di elementi con implementazioni lente Object.equals (java.lang.Object) o Object.hashCode(). È possibile ottenere prestazioni migliori avendo il proprio elemento digita nella cache i propri codici hash e facendo uso dei valori memorizzati nella cache per cortocircuitare un algoritmo di equals lento. " Questo è certamente un problema con 'String'. Infine, l'implementazione di 'ImmutableMap' è in gran parte la stessa di' HashMap'. –
Se lo desideri, puoi provare alcuni dei benchmark di Guava: https://code.google.com/p/guava-libraries/source/browse/guava-tests/benchmark/com/google/common/collect/MapBenchmark. java –
Inoltre, "gestire i costi della memoria" potrebbe comportare un aumento significativo del carico del GC, che può rallentare il programma tanto quanto un'implementazione più lenta ma più compatta. Non c'è davvero alcun sostituto per la profilazione con la tua specifica applicazione reale. –