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

user=> (def lg (range 1 10000000))
user=> (time (def rs (doall (pmap #(* % %) lg))))

"Elapsed time: **125739.056** msecs"

# -------------------------------------------------------
user=> (def lg (range 1 10000000))
user=> (time (def rs (doall (map #(* % %) lg))))

"Elapsed time: **5804.485** msecs"

**PS: the machine has 8 cores**

最佳答案

对于每个并行处理任务,由于任务协调而导致一定量的开销。 pmap将映射函数分别应用于不同线程中的每个元素。由于使用了pmap返回的惰性序列,因此使用者线程必须与生产者线程协调。定义pmap的方式,这种开销发生在每个产生的元素上。

考虑到这一点,当您使用pmap计算一个简单函数(例如,对数字进行平方运算,如您的示例中所示)时,线程协调其 Activity 所花费的时间就浪费了实际计算该值所花费的时间。就像文档字符串所说的那样,pmap“仅对其中f的时间支配协调开销的计算密集型函数有用”(添加了轻重缓急)。在这些情况下,不管您有多少个内核,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有效性的另一种方法是将工作分配到整个算法的不同位置。例如,假设我们对计算成对的点之间的欧式距离感兴趣。虽然对平方函数进行并行化已被证明是无效的,但对整个距离函数进行并行化可能会有些运气。实际上,我们希望将任务划分到更高的层次,但这就是要点。

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

关于dictionary - Clojure PMap与Map,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/19953036/

10-11 19:13