Sto lavorando a un progetto in cui sto elaborando molti tweet; l'obiettivo è rimuovere i duplicati mentre li elaboro. Ho gli ID tweet, che arrivano come stringhe del formato "166471306949304320"
Java: ottimizzazione dell'hashset per il rilevamento di duplicati su larga scala
Per questo ho usato uno HashSet<String>
, che funziona bene per un po '. Ma nel momento in cui raggiungo circa 10 milioni di oggetti, mi sto drasticamente impantanando e alla fine ottengo un errore di GC, presumibilmente dal rehashing. Ho provato a definire una migliore dimensione/di carico con
tweetids = new HashSet<String>(220000,0.80F);
e che gli permette di ottenere un po 'più lontano, ma è ancora estremamente lento (di circa 10 milioni sta prendendo 3x del tempo per elaborare). Come posso ottimizzare questo? Dato che ho un'idea approssimativa di quanti elementi dovrebbero essere nel set alla fine (in questo caso, circa 20-22 milioni) dovrei creare un HashSet che rihashes solo due o tre volte, o sarebbe il sovraccarico per tale il set ha subito troppe penalità? Le cose funzionerebbero meglio se non usassi una stringa, o se definissi una funzione HashCode diversa (che, in questo caso di una particolare istanza di una stringa, non sono sicuro di come fare)? Questa parte del codice di implementazione è sotto.
tweetids = new HashSet<String>(220000,0.80F); // in constructor
duplicates = 0;
...
// In loop: For(each tweet)
String twid = (String) tweet_twitter_data.get("id");
// Check that we have not processed this tweet already
if (!(tweetids.add(twid))){
duplicates++;
continue;
}
SOLUZIONE
grazie ai vostri suggerimenti, ho risolto. Il problema era la quantità di memoria richiesta per le rappresentazioni di hash; primo, HashSet<String>
era semplicemente enorme e non richiesto perché il String.hashCode()
è esorbitante per questa scala. Successivamente ho provato un Trie, ma si è bloccato a poco più di 1 milione di voci; la riallocazione degli array era problematica. Ho usato uno HashSet<Long>
per migliorare l'effetto e quasi ce l'ho fatto, ma la velocità è decaduta e alla fine si è schiantato sull'ultima parte dell'elaborazione (circa 19 milioni). La soluzione è arrivata con l'uscita dalla libreria standard e utilizzando Trove. Ha terminato 22 milioni di record con pochi minuti in più rispetto al non aver controllato affatto i duplicati. implementazione finale era semplice, e si presentava così:
import gnu.trove.set.hash.TLongHashSet;
...
TLongHashSet tweetids; // class variable
...
tweetids = new TLongHashSet(23000000,0.80F); // in constructor
...
// inside for(each record)
String twid = (String) tweet_twitter_data.get("id");
if (!(tweetids.add(Long.parseLong(twid)))) {
duplicates++;
continue;
}
Che ne dici di considerare gli ID come numeri, trovare un buon valore di base e lavorare con le differenze? Si potrebbe quindi utilizzare un 'HashSet', che dovrebbe sovraperformare le stringhe; potresti anche usare la libreria Trove per lavorare con i primitivi. –
Non puoi semplicemente aumentare la dimensione del tuo heap? – assylias
Se sai che il set conterrà 22 milioni di elementi, perché non crei un HashSet con una capacità di 22_000_000/0,75 dall'inizio? Ciò impedirebbe qualsiasi restituzione. –