2013-02-11 53 views
8

我想知道如何使用clojure並行地將大量的序列有效地應用到一個簡單的操作。我希望能夠使用並行解決方案來利用我的機器上的多個內核來實現一些加速。如何計算使用Clojure並行數字的大數列表的總和

我正在嘗試將pmap與partition-all結合使用,以減少爲輸入序列中的每個項目創建未來的開銷。不幸的是,partition-all強制每個分區seq的完整評估。這會在我的機器上導致OutOfMemoryError。

(defn sum [vs] 
    (reduce + vs)) 

(def workers 
    (+ 2 (.. Runtime getRuntime availableProcessors))) 

(let 
    [n 80000000 
    vs (range n)] 

    (time (sum vs)) 
    (time (sum (pmap sum (partition-all (long (/ n workers)) vs))))) 

如何申請一筆較大的輸入集,並擊敗串行實現的性能?

解決方案

感謝@Arthur Ulfeldt您指出減速機庫。這是使用減速器的解決方案。該代碼顯示了在多核機器上運行時預期的性能改進。 (注:我已經改變VS是一個函數,使時間更加準確)

(require '[clojure.core.reducers :as r]) 

(let 
    [n 80000000 
    vs #(range n)] 

    (time (reduce + (vs))) 
    (time (r/fold + (vs))) 
+0

是塊中的錯誤的方式周圍? '(分區 - 所有工人vs)'創建長度爲'工作人員'的(/ n工人)序列。你不想'(分區 - 所有(長(/ n工人))vs)'? – 2013-02-11 18:44:15

+0

@ A.Webb,感謝您的糾正。我會修改這個問題。此修復有助於使並行版本更快一些,但它仍然無法擊敗串行實現,並且它仍然在非常大的輸入上耗盡內存。 – 2013-02-11 19:15:39

回答

8

當使用PMAP我發現相當大塊需要克服開關和未來的開銷試塊大小爲10,000,操作速度可達+。潛在的收益受到生成塊的開銷的限制。這會產生一個最佳值,以平衡可用內核和製作塊所需的時間。在這種情況下,+作爲工作負載,我無法比單線程選項更快。

如果你有興趣做這個沒有PMAP和潛在使用叉子/加入檢查出新的(ISH)reducers library

的OOM情況來自於第一個測試從(range n)實現懶惰序列然後將其保持所以它可以傳遞給第二個序列。

如果我做+功能多定義一個slow+功能速度較慢,使用單個線程,PMAP過塊,和減速器之間的性差異W/forkJoin成爲visable:

user> *clojure-version*                
{:major 1, :minor 5, :incremental 0, :qualifier "RC15"} 
(require '[clojure.core.reducers :as r]) 

(def workers 
    (+ 2 (.. Runtime getRuntime availableProcessors))) 

(defn slow+ 
    ([] 0) 
    ([x] x) 
    ([x y] (reduce + (range 100000)) (+ x y))) 

(defn run-test [] 
    (let [n 8000] 
    (time (reduce slow+ (range n))) 
    (time (reduce slow+ (pmap #(reduce slow+ %) (partition-all (* workers 100) (range n))))) 
    (time (r/fold slow+ (vec (range n)))))) 

user> (run-test) 
"Elapsed time: 28655.951241 msecs" ; one thread 
"Elapsed time: 6975.488591 msecs" ; pmap over chunks 
"Elapsed time: 8170.254426 msecs" ; using reducer