我在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/