Подтвердить что ты не робот

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

Использование 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 правильным?

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

4b9b3361

Ответ 1

Этот вопрос: how-to-efficiently-apply-a-medium-weight-function-in-parallel также решает эту проблему в очень близком контексте.

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

Ответ 2

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

Пример Rich попробовать его.

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

Ответ 3

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

Ответ 4

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

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