我正在尝试解析一个包含一百万行的文件,每一行都是一个 json 字符串,其中包含有关一本书的一些信息(作者、内容等)。我正在使用iota加载文件,因为OutOfMemoryError
如果我尝试使用slurp
. 我也在使用cheshire来解析字符串。该程序只需加载一个文件并计算所有书籍中的所有单词。
我的第一次尝试包括pmap
做繁重的工作,我认为这基本上会利用我所有的 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))))
虽然它似乎确实使用了所有核心,但每个核心很少使用超过 50% 的容量,我的猜测是它与 pmap 的批量大小有关,所以我偶然发现了一些相对较老的问题,其中一些评论引用了clojure.core.reducers
库.
我决定使用以下方法重写函数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))))
但是 cpu 使用率更差,与之前的实现相比,它甚至需要更长的时间才能完成:
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
我究竟做错了什么?解析大文件时,mmap loading + reducers 是正确的方法吗?
编辑:这是我正在使用的文件。
EDIT2:以下是使用iota/seq
而不是的时间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