Sto cercando di analizzare un file con un milione di righe, ogni riga è una stringa json con alcune informazioni su un libro (autore, contenuto ecc.). Sto usando iota per caricare il file, poiché il mio programma lancia un OutOfMemoryError
se provo a usare slurp
. Sto anche usando cheshire per analizzare le stringhe. Il programma carica semplicemente un file e conta tutte le parole in tutti i libri.Perché pmap | reducers/map non usa tutti i core della CPU?
Il mio primo tentativo ha incluso pmap
per eseguire il lavoro pesante, ho pensato che essenzialmente avrebbe utilizzato tutti i miei core della CPU.
(ns multicore-parsing.core
(:require [cheshire.core :as json]
[iota :as io]
[clojure.string :as string]
[clojure.core.reducers :as r]))
(defn words-pmap
[filename]
(letfn [(parse-with-keywords [str]
(json/parse-string str true))
(words [book]
(string/split (:contents book) #"\s+"))]
(->>
(io/vec filename)
(pmap parse-with-keywords)
(pmap words)
(r/reduce #(apply conj %1 %2) #{})
(count))))
Anche se sembra utilizzare tutti i core, ogni core utilizza raramente più del 50% della sua capacità, la mia ipotesi è che ha a che fare con la dimensione del lotto di pmap e così mi sono imbattuto in relatively old question dove alcuni commenti fare riferimento alla libreria clojure.core.reducers
.
ho deciso di riscrivere la funzione utilizzando reducers/map
:
(defn words-reducers
[filename]
(letfn [(parse-with-keywords [str]
(json/parse-string str true))
(words [book]
(string/split (:contents book) #"\s+"))]
(->>
(io/vec filename)
(r/map parse-with-keywords)
(r/map words)
(r/reduce #(apply conj %1 %2) #{})
(count))))
Ma l'utilizzo della CPU è peggio, e ancora richiede più tempo per terminare rispetto alla precedente realizzazione:
multicore-parsing.core=> (time (words-pmap "./dummy_data.txt"))
"Elapsed time: 20899.088919 msecs"
546
multicore-parsing.core=> (time (words-reducers "./dummy_data.txt"))
"Elapsed time: 28790.976455 msecs"
546
Che cosa sono io fare male? Mmap sta caricando + riduzioni l'approccio corretto durante l'analisi di un file di grandi dimensioni?
MODIFICA: this è il file che sto utilizzando.
EDIT2: Qui ci sono i tempi con iota/seq
invece di iota/vec
:
multicore-parsing.core=> (time (words-reducers "./dummy_data.txt"))
"Elapsed time: 160981.224565 msecs"
546
multicore-parsing.core=> (time (words-pmap "./dummy_data.txt"))
"Elapsed time: 160296.482722 msecs"
546
Sembra 'io/vec' scansiona l'intero file per costruire un indice di dove le linee sono. Ottenete risultati diversi se provate 'io/seq'? –
@NathanDavis Ho appena provato, i tempi sono peggiori. fammi aggiornare la domanda – eugecm
[Questo talk] (https://www.youtube.com/watch?v = BzKjIk0vgzE) di Leon Barrett, autore di [Claypoole] (https://github.com/TheClimateCorporation/claypoole), potrebbe avere alcune informazioni rilevanti. Spiega 'pmap' in dettaglio, incluso il motivo per cui spesso non satura la CPU, e un po 'sul perché nutrire un' pmap' in un altro può avere risultati sorprendenti. Inoltre, se sei principalmente alla ricerca di un modo per saturare la tua CPU, Claypoole potrebbe essere esattamente ciò di cui hai bisogno. –