Лучшая альтернатива pmap в Clojure для распараллеливания умеренно недорогих функций над большими данными? - PullRequest
29 голосов
/ 20 января 2010

Использование clojure У меня очень большой объем данных в последовательности, и я хочу обрабатывать их параллельно, с относительно небольшим количеством ядер (от 4 до 8).

Самое простое, что нужно сделать, это использовать pmap вместо map, чтобы отобразить мою функцию обработки в последовательности данных. Но координационные издержки приводят к чистым потерям в моем случае .

Я думаю, причина в том, что pmap предполагает, что функция, сопоставленная с данными, очень дорогая . Глядя на исходный код pmap, он, по-видимому, строит future для каждого элемента последовательности по очереди, поэтому каждый вызов функции происходит в отдельном потоке (циклически изменяя количество доступных ядер).

Вот соответствующий фрагмент исходного кода pmap:

(defn pmap
  "Like map, except f is applied in parallel. Semi-lazy in that the
  parallel computation stays ahead of the consumption, but doesn't
  realize the entire result unless required. Only useful for
  computationally intensive functions where the time of f dominates
  the coordination overhead."
  ([f coll]
   (let [n (+ 2 (.. Runtime getRuntime availableProcessors))
         rets (map #(future (f %)) coll)
         step (fn step [[x & xs :as vs] fs]
                (lazy-seq
                 (if-let [s (seq fs)]
                   (cons (deref x) (step xs (rest s)))
                   (map deref vs))))]
     (step rets (drop n rets))))
  ;; multi-collection form of pmap elided

В моем случае отображенная функция не так уж дорога, но последовательность огромна (миллионы записей). Я думаю, что стоимость создания и разыменования такого большого количества фьючерсов - это то, где параллельная прибыль теряется в накладных расходах.

Правильно ли мое понимание pmap?

Есть ли лучший шаблон в укупорке для такого рода более дешевой, но массово повторяющейся обработки, чем pmap? Я подумываю о том, чтобы как-то разбить последовательность данных, а затем запустить потоки на больших кусках. Это разумный подход и какие идиомы clojure будут работать?

Ответы [ 4 ]

20 голосов
/ 20 января 2010

Этот вопрос: как эффективно применять параллельные функции среднего веса также решает эту проблему в очень похожем контексте.

Текущий лучший ответ - использовать partition, чтобы разбить его на куски. затем отобразите функцию карты на каждый фрагмент. затем рекомбинируйте результаты. Карта-свертка-стиль.

5 голосов
/ 21 января 2010

К сожалению, пока нет правильного ответа, но кое-что, на что следует обратить внимание в будущем, - это работа Рича с библиотекой fork / join, появившейся в Java 7. Если вы посмотрите на его ветку Par на github, он проделал некоторую работу с ним и в заключение Я видел, что ранние возвращения были удивительными.

Пример того, как Рич это попробовал.

http://paste.lisp.org/display/84027

2 голосов
/ 24 сентября 2013

Работа с fork / join, упомянутая в предыдущих ответах на эту и подобные темы, в конечном итоге принесла свои плоды в виде библиотеки redurs , которую, вероятно, стоит посмотреть.

0 голосов
/ 20 января 2010

Вы можете использовать какую-то карту / уменьшить, реализованную вручную. Также взгляните на swarmiji framework.

«Распределенная вычислительная система, которая помогает писать и запускать код Clojure параллельно - по всем ядрам и процессорам»

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...