clojure pmap vs map

时间:2013-11-13 11:50:11

标签: map clojure pmap

我在cojure REPL中测试了clojure函数贴图和pmap,如下所示。这让我感到困惑:为什么并行pmap比map慢?

用户=> (def lg(范围1 10000000))

用户=> (时间(def rs(doall(pmap#(* %%)lg))))

“经过时间: 125739.056 msecs”

----------------------------------------------- --------

用户=> (def lg(范围1 10000000))

用户=> (时间(def rs(doall(map#(* %%)lg))))

“经过时间: 5804.485 msecs”

PS:机器有8个核心

3 个答案:

答案 0 :(得分:16)

对于每个并行处理任务,由于任务协调,会产生一些开销。 pmap将映射函数分别应用于不同线程中的每个元素。由于pmap返回的延迟序列被消耗,消费者线程必须与生产者线程协调。定义pmap的方式,对每个生成的元素都会产生这种开销。

考虑到这一点,当你使用pmap计算一个简单的函数(比如平方数字,如你的例子)时,线程协调其活动所花费的时间会淹没实际所需的时间计算价值。正如文档字符串所说,pmap“仅对计算密集型函数有用,其中f的时间支配协调开销”(empasis添加)。在这些情况下,无论您拥有多少核心,pmap所需的时间都会超过map

要真正看到pmap带来的好处,您必须选择“更难”的问题。在某些情况下,这可能就像将输入序列划分为块一样简单。然后可以使用pmap处理块序列,然后运行concat以获得最终输出。

例如:

(defn chunked-pmap [f partition-size coll]
  (->> coll                           ; Start with original collection.

       (partition-all partition-size) ; Partition it into chunks.

       (pmap (comp doall              ; Map f over each chunk,
                   (partial map f)))  ; and use doall to force it to be
                                      ; realized in the worker thread.

       (apply concat)))               ; Concatenate the chunked results
                                      ; to form the return value.

但是,在序列分区和最后连接块时也存在开销。例如,至少在我的计算机上,chunked-pmap仍为您的示例提供了大量的map。不过,它可能对某些功能有效。

提高pmap效率的另一种方法是将工作划分到整个算法的不同位置。例如,假设我们对计算点对之间的欧氏距离感兴趣。虽然并行化方形函数已被证明是无效的,但我们可能会有一些运气并行化整个距离函数。实际上,我们希望将任务划分到更高的级别,但这就是它的要点。

简而言之,并行算法的性能对任务分区的方式很敏感,并且您选择的级别对于测试而言过于精细。

答案 1 :(得分:3)

Rörd是正确的,使用pmap有很大的开销。考虑使用reducer:

(def l (range 10000000))

(time (def a (doall (pmap #(* % %) l))))
"Elapsed time: 14674.415781 msecs"

(time (def a (doall (map #(* % %) l))))
"Elapsed time: 1119.107447 msecs"

(time (def a (doall (into [] (r/map #(* % %) l)))))
"Elapsed time: 1049.754652 msecs"

答案 2 :(得分:2)

创建线程,在它们之间分配工作负载以及重新组合结果需要一些开销。您将需要一个运行时间远远超过#(* % %)的功能,以便从pmap看到速度提升(当然,它还取决于您未在CPU中指定的CPU核心数量。问题)。