使用clojure我在序列中有非常大量的数据,我想并行处理它,核心数量相对较少(4到8)。
最简单的方法是使用pmap
代替map
,将处理函数映射到数据序列上。 但协调开销会导致我的案件净损失。
我认为原因是pmap
假设跨数据映射的功能非常昂贵。看看pmap的源代码,它似乎依次为序列的每个元素构造一个future
,因此函数的每次调用都发生在一个单独的线程上(循环遍历可用内核的数量)。
以下是pmap的相关部分:
(defn pmap
"Like map, except f is applied in parallel. Semi-lazy in that the
parallel computation stays ahead of the consumption, but doesn't
realize the entire result unless required. Only useful for
computationally intensive functions where the time of f dominates
the coordination overhead."
([f coll]
(let [n (+ 2 (.. Runtime getRuntime availableProcessors))
rets (map #(future (f %)) coll)
step (fn step [[x & xs :as vs] fs]
(lazy-seq
(if-let [s (seq fs)]
(cons (deref x) (step xs (rest s)))
(map deref vs))))]
(step rets (drop n rets))))
;; multi-collection form of pmap elided
在我的情况下,映射函数并不昂贵,但序列很大(数百万条记录)。我认为创建和解除引用许多未来的成本是平行增益在开销中丢失的原因。
我对pmap
的理解是否正确?
与pmap
相比,这种低成本但大规模重复处理的clojure是否有更好的模式?我正在考虑以某种方式分块数据序列,然后在更大的块上运行线程。 这是一种合理的方法,而且哪些语言可以起作用?
答案 0 :(得分:20)
这个问题:how-to-efficiently-apply-a-medium-weight-function-in-parallel也在非常相似的背景下解决了这个问题。
目前最好的答案是使用 partition
将其分解为块。然后将map函数映射到每个块上。然后重新组合结果。地图,减少式。
答案 1 :(得分:5)
可悲的是,还没有一个有效的答案,但是将来需要注意的是Rich在Java 7中使用fork / join库的工作。如果你在github上查看他的Par分支,他已经完成了一些工作,并且最后我看到早期的回报令人惊讶。
Rich尝试它的例子。
答案 2 :(得分:2)
前面关于这个和类似线程的答案中提到的fork / join工作最终会成为reducers库的结果,这可能值得一看。
答案 3 :(得分:0)
你可以使用手工实现的某种map / reduce。另请参阅swarmiji框架。
“有助于并行编写和运行Clojure代码的分布式计算系统 - 跨核心和处理器”