Clojure параллельное отображение и бесконечные последовательности - PullRequest
7 голосов
/ 12 августа 2010

Допустим, я определяю последовательность всех натуральных чисел следующим образом:

(def naturals (iterate inc 0))

Я также определяю функцию, отображающую натуральные числа в ноль, для вычисления которой требуется некоторое время, вот так:

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

Обратите внимание на время вычисления для вычисления следующих s-выражений, измеренное как clojure.core/time.

(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 мсек

pmap в ~ 6 раз быстрее при вызове с явным диапазоном, чем с частью натуральных чисел.

Так как (= (range 30) (take 30 naturals)) возвращает true и оба объекта имеют тип clojure.lang.LazySeq, и clojure вычисляет все аргументы функции перед вызовом функции, как объяснить вышеупомянутые детали синхронизации?

1 Ответ

8 голосов
/ 12 августа 2010

Я думаю, это из-за этого:

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 предмета. Вот как много элементов отбирается за кусок для диапазона. Chunked seqs заранее оценивает кучу элементов, чтобы повысить производительность. В этом случае это выглядит так, как будто pmap порождает 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 x 500 мс, из которых 4 означает, что из 32 кусков требуется до 100).

С другой стороны, вы, возможно, не захотите работать с чанкингом. 32 потока одновременно - это много. См. этот вопрос , где приведены примеры того, как отсоединить последовательность.

...