如何使用Clojure并行计算大量数字的总和
我试图找出如何使用clojure高效地将简单操作并行应用于大型序列。我希望能够使用并行解决方案来利用我的机器上的多核来实现一些加速 我试图将pmap与分区all结合使用,以减少为输入序列中的每个项目创建未来的开销。不幸的是,partitionall强制对每个分区seq进行完整的计算。这会在我的计算机上导致OutOfMemory错误如何使用Clojure并行计算大量数字的总和,clojure,parallel-processing,Clojure,Parallel Processing,我试图找出如何使用clojure高效地将简单操作并行应用于大型序列。我希望能够使用并行解决方案来利用我的机器上的多核来实现一些加速 我试图将pmap与分区all结合使用,以减少为输入序列中的每个项目创建未来的开销。不幸的是,partitionall强制对每个分区seq进行完整的计算。这会在我的计算机上导致OutOfMemory错误 (defn sum [vs] (reduce + vs)) (def workers (+ 2 (.. Runtime getRuntime availab
(defn sum [vs]
(reduce + vs))
(def workers
(+ 2 (.. Runtime getRuntime availableProcessors)))
(let
[n 80000000
vs (range n)]
(time (sum vs))
(time (sum (pmap sum (partition-all (long (/ n workers)) vs)))))
如何将sum应用于大型输入集,并击败串行实现的性能
解决方案
感谢@Arthur Ulfeldt指出减速器库。这是使用还原剂的解决方案。此代码显示在多核机器上运行时预期的性能改进。(注:我已将vs更改为一个函数,以使计时更准确)
当使用pmap时,我发现需要相当大的块来克服切换和未来的开销。请尝试10000块大小的块,以便以
+
的速度进行操作。潜在收益受到生成块的开销的限制。这将产生一个最佳值,该值平衡了可用内核和生成块所需的时间。在这种情况下,+
作为工作负载,我无法使其比单线程选项更快
如果您有兴趣在没有pmap的情况下执行此操作,并可能使用fork/join,请查看新的(ish)
OOM情况来自第一个测试,该测试实现了(范围n)
中的延迟序列,然后保留该序列,以便将其传递给第二个序列
如果我通过定义一个slow+
函数使+函数慢得多,并使用该函数可以看到单线程、块上的pmap和带forkJoin的减速器之间的差异:
user> *clojure-version*
{:major 1, :minor 5, :incremental 0, :qualifier "RC15"}
(require '[clojure.core.reducers :as r])
(def workers
(+ 2 (.. Runtime getRuntime availableProcessors)))
(defn slow+
([] 0)
([x] x)
([x y] (reduce + (range 100000)) (+ x y)))
(defn run-test []
(let [n 8000]
(time (reduce slow+ (range n)))
(time (reduce slow+ (pmap #(reduce slow+ %) (partition-all (* workers 100) (range n)))))
(time (r/fold slow+ (vec (range n))))))
user> (run-test)
"Elapsed time: 28655.951241 msecs" ; one thread
"Elapsed time: 6975.488591 msecs" ; pmap over chunks
"Elapsed time: 8170.254426 msecs" ; using reducer
这些块是不是走错了方向<代码>(划分所有工作人员对)创建
(/n个工作人员)
长度序列工作人员
。您不想要(分区所有(长(/n个工作人员))vs)
?@A.Webb,谢谢您的更正。我会修正这项质询。此修复有助于使并行版本更快一些,但它仍然无法击败串行实现,并且在非常大的输入上仍然会耗尽内存。
user> *clojure-version*
{:major 1, :minor 5, :incremental 0, :qualifier "RC15"}
(require '[clojure.core.reducers :as r])
(def workers
(+ 2 (.. Runtime getRuntime availableProcessors)))
(defn slow+
([] 0)
([x] x)
([x y] (reduce + (range 100000)) (+ x y)))
(defn run-test []
(let [n 8000]
(time (reduce slow+ (range n)))
(time (reduce slow+ (pmap #(reduce slow+ %) (partition-all (* workers 100) (range n)))))
(time (r/fold slow+ (vec (range n))))))
user> (run-test)
"Elapsed time: 28655.951241 msecs" ; one thread
"Elapsed time: 6975.488591 msecs" ; pmap over chunks
"Elapsed time: 8170.254426 msecs" ; using reducer