Un approccio multielaborazione per elencare le occorrenze di parole in un file di testo

4

Un problema classico: leggere le parole da un file di testo ed elencare l'occorrenza di ogni parola univoca nel file.

Ho risolto il problema utilizzando una mappa hash, ma come si può migliorare la prestazione? Ho provato a leggere più righe di quel file usando i thread, ma anche quello sarebbe come un collo di bottiglia e ci sono possibilità di race condition in una hashmap. L'utilizzo di HashMap concorrente causerebbe un collo di bottiglia. Quale sarebbe un approccio multithread ideale?

    
posta Nilesh 16.01.2017 - 15:53
fonte

2 risposte

3

Supponendo di poter dividere in modo efficiente i file in blocchi (ad esempio, gruppi di linee), puoi provare ad associare alcuni blocchi a ciascun thread e creare una hashmap per ciascuno dei tuoi thread. Non appena sono terminati due thread, puoi unire le loro hashmap in una nuova nuova hashmap (l'hashmap non è altro che una monade), e procedere fino a ottenere una singola hashmap finale contando le parole per l'intero file.

Devi sintonizzare alcuni parametri per trovare il compromesso più interessante tra granularità fine ed efficienza: numero di thread, numero di blocchi, ecc.

Un'implementazione probabilmente subottimale ma semplice sarebbe quella di aspettare che tutte le hashmaps siano state create prima di unirle tutte nello stesso momento. Un tentativo non controllato in Java 8:

Function<String, Map<String, Long>> countWords = (block) -> {
   Map<String, Long> ret = new HashMap<String, Long>();
   for(String word : block.split(" ")){
      ret.merge(word, 1, (a,b) -> a+b);
   }

   return ret;
};

BinaryOperator<Map<String, Long>> combine = (m1, m2) -> {
   Map<String, Long> m3 = new HashMap<>(m1);
   m2.forEach((k, v) -> m3.merge(k, v, (a,b) -> a+b);

   return m3;
};

Stream<String> blocks = file.getLineBlocks().parallelStream();
Stream<Map<String, Long>> counts = blocks.map(block -> countWords(blocks));
Map<String, Long> count = counts.reduce(new HashMap<String, Long>(), combine);    
    
risposta data 17.01.2017 - 09:48
fonte
3

Molte implementazioni di tabelle hash non sono thread-safe. Altre implementazioni potrebbero essere thread-safe, ma lente. Altre implementazioni potrebbero essere thread-safe, lente se effettivamente accessibili da più thread contemporaneamente e veloci se accessibili da più thread, ma non allo stesso tempo. Dal momento che trovare le parole richiede pochissimo sforzo, è probabile che qualsiasi implementazione multithreading esegua il martellamento della tabella hash, quindi la maggior parte delle implementazioni thread-safe sarebbero lente.

Potresti implementare una tabella hash veloce sotto multithreading a patto che non ci siano scritture (perché in questo caso il multi-threading non è un problema). Quindi crea il tuo elenco di parole multi-threaded e puoi verificare se le voci sono già presenti nella tabella hash multithreaded e se hai una certa quantità di nuove parole, le aggiungi a thread singolo tutte insieme. Diciamo che hai 10 milioni di parole ma solo 100.000 distinte, quindi 9.900.000 volte troverai "la parola è già presente" con il tuo codice multithread.

Naturalmente, in questa situazione, avere 4 thread, ognuno dei quali legge 2.500.000 parole e riempire ciascuna la propria tabella hash con 90.000 voci, e quindi unire le tabelle hash, sarebbe molto più semplice.

    
risposta data 17.01.2017 - 10:32
fonte

Leggi altre domande sui tag