2010-08-12 22 views
7

Diciamo che io definisco la sequenza di tutti i numeri naturali nel seguente modo:Clojure mappatura parallela e sequenze infinite

(def naturals (iterate inc 0)) 

Ho anche definire una funzione di mappatura dei prodotti naturali a zero che vuole un po 'per calcolare in questo modo:

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

Nota il tempo di calcolo per evaulate seguenti s-espressioni come misurato da clojure.core/time.

(dorun (map hard-comp (range 30))) ; 15010,367496 msec

(dorun (pmap hard-comp (range 30))) ; 537.044554 msec

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

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

(doall (take 30 naturals)) ; 0.385724 msec

(range 30); 0,159374 msecs

pmap è ~ 6 volte più veloce quando viene chiamato con un intervallo esplicito rispetto a una sezione dei naturali.

Poiché (= (range 30) (take 30 naturals)) restituisce true e entrambi gli oggetti sono di tipo clojure.lang.LazySeq e il clojure evaula tutti gli argomenti di una funzione prima di chiamare la funzione, come possono essere spiegati i dettagli di temporizzazione di cui sopra?

risposta

8

La mia ipotesi è che è a causa di questo:

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 

Prova questa:

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

Nota che salta di 32 voci alla volta. Ecco quanti elementi vengono catturati per pezzo per un intervallo. Seqs Chunked pre-valutare un gruppo di elementi in anticipo per aumentare le prestazioni. In questo caso sembra che pmap generosamente generi 32 thread non appena tenti di prendere anche solo un elemento dall'intervallo.

Puoi sempre riempire i tuoi naturali in un vettore per ottenere un comportamento di chunking.

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" 

(Si noti che il tempo è di circa 4 x 500 ms, 4 essendo il numero di pezzi di 32 che serve per arrivare a 100.)

D'altra parte, non si potrebbe desiderare chunking comportamento. 32 thread alla volta è molto. Vedi this question per esempi su come annullare il chunkify su un seq.