clojure pmap vs map

cat*_*ter 5 clojure map pmap

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

user =>(def lg(范围1 10000000))

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

"经过的时间:125739.056毫秒 "

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

user =>(def lg(范围1 10000000))

user =>(time(def rs(doall(map#(*%%)lg))))

"经过的时间:5804.485毫秒 "

PS:机器有8个核心

Nat*_*vis 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.
Run Code Online (Sandbox Code Playgroud)

但是,还有一个分配序列和最后连接块的开销.例如,至少在我的机器上,对于您的示例而言,chunked-pmap仍然表现map不佳.不过,它对某些功能可能有效.

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

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


Rör*_*örd 2

创建线程、在线程之间分配工作负载以及重新组装结果会产生一些开销。您将需要一个运行时间明显长于#(* % %)速度改进的函数pmap(当然,它也取决于您在问题中未指定的 CPU 核心数量)。