[英]clojure pmap vs map
我在 cojure REPL 中測試了 clojure 函數 map 和 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
計算一個簡單的函數時(例如在您的示例中對數字進行平方),線程協調其活動所需的時間會淹沒實際計算該值所需的時間。 正如文檔字符串所說, 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
有效性的方法是在整個算法的不同位置對工作進行分區。 例如,假設我們對計算點對之間的歐幾里德距離感興趣。 雖然並行化平方函數已被證明是無效的,但我們可能有幸並行化整個距離函數。 實際上,我們希望在更高的層次上划分任務,但這就是它的要點。
簡而言之,並行算法的性能對任務的分區方式很敏感,並且您選擇的級別對於您的測試來說過於細化。
Rörd 是正確的,使用 pmap 有很大的開銷。 考慮改用減速器:
(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"
創建線程、在它們之間分配工作負載並重新組合結果會產生一些開銷。 您將需要一個運行時間明顯長於#(* % %)
的函數才能看到pmap
的速度提升(當然,它也取決於您未在問題中指定的 CPU 內核數)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.