2010-08-12 30 views
7

比方說,我定義所有自然數按以下方式順序:Clojure的並行映射和無窮序列

(def naturals (iterate inc 0)) 

我也定義一個函數映射土黃爲零需要一段時間來計算,如下所示:

(defn hard-comp [_] (Thread/sleep 500)) 

注計算時間由clojure.core/time測量evaulate以下s表達式。

(dorun (map hard-comp (range 30))) ; 15010.367496毫秒

(dorun (pmap hard-comp (range 30))) ; 537.044554毫秒

(dorun (map hard-comp (doall (take 30 naturals))))) ; 15009.488499毫秒

(dorun (pmap hard-comp (doall (take 30 naturals)))) ;3004.499013毫秒

(doall (take 30 naturals)) ; 0.385724毫秒

(range 30); 0.159374 msecs

pmap當用明確的範圍調用比使用自然段更快〜6倍。

由於(= (range 30) (take 30 naturals))返回true,並且兩個對象的類型都是clojure.lang.LazySeq,並且clojure在調用該函數前會對函數的所有參數進行蒸發,所以上述時間細節如何解釋?

回答

8

我的猜測是,這是由於這樣的:

user> (chunked-seq? (seq (range 30))) 
true 
user> (chunked-seq? (seq (take 30 naturals))) 
false 
user> (class (next (range 30))) 
clojure.lang.ChunkedCons 
user> (class (next (take 30 naturals))) 
clojure.lang.Cons 

試試這個:

user> (defn hard-comp [x] (println x) (Thread/sleep 500)) 
#'user/hard-comp 
user> (time (dorun (pmap hard-comp (range 100)))) 

注意,它跳到32個項目在同一時間。這就是一個範圍內每塊大小抓取的元素數量。分塊序列提前預先評估一堆項目以提高性能。在這種情況下,只要嘗試抓取範圍中的一個元素,它就會看起來像pmap chunkily產生32個線程。

你總是可以將你的自然元素填充到矢量中以獲得分塊行爲。

user> (time (dorun (pmap hard-comp (range 100)))) 
"Elapsed time: 2004.680192 msecs" 
user> (time (dorun (pmap hard-comp (vec (take 100 naturals))))) 
"Elapsed time: 2005.887754 msecs" 

(注意,時間大約爲4×500毫秒,4是需要多少的32塊去100)

在另一方面,你可能不想分塊的行爲。一次32個線程是很多。請參閱this question以瞭解如何拆分seq的示例。