Документация по функции pmap
оставляет мне интересно, насколько она эффективна для чего-то вроде сбора коллекции XML-каналов через Интернет. Я не знаю, сколько одновременных операций выборки pmap будет появляться и что будет максимальным.
Сколько потоков выполняет Clojure функция pmap для операций URL-выборки?
Ответ 1
Если вы проверите источник, который вы видите:
> (use 'clojure.repl)
> (source 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."
{:added "1.0"}
([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))))
([f coll & colls]
(let [step (fn step [cs]
(lazy-seq
(let [ss (map seq cs)]
(when (every? identity ss)
(cons (map first ss) (step (map rest ss)))))))]
(pmap #(apply f %) (step (cons coll colls))))))
(+ 2 (.. Runtime getRuntime availableProcessors))
- это большой ключ. pmap будет захватывать первые (+ 2 processors)
части работы и запускать их асинхронно через future
. Поэтому, если у вас есть 2 ядра, он будет запускать 4 части работы за раз, стараясь немного опередить вас, но max должен быть 2 + n.
future
в конечном итоге использует пул потоков ввода-вывода агента, который поддерживает неограниченное количество потоков. Он будет расти по мере того, как на него набрасывается работа и сжимается, если потоки не используются.
Ответ 2
Основываясь на замечательном ответе Алексея, объясняющем, как работает pmap, вот мое предложение для вашей ситуации:
(doall
(map
#(future (my-web-fetch-function %))
list-of-xml-feeds-to-fetch))
Обоснование:
- Вы хотите как можно больше работы в полете, так как большинство будет блокировать сетевое IO.
- Будущее будет отключать асинхронную часть работы для каждого запроса, которая будет обрабатываться в пуле потоков. Вы можете позволить Clojure позаботиться об этом разумно.
- Сопровождение на карте будет заставлять оценивать полную последовательность (то есть запуск всех запросов).
- Ваш основной поток может начать разыгрывать фьючерсы сразу, и поэтому он может продолжать продвигаться по мере возврата отдельных результатов.
Ответ 3
Нет времени для написания длинного ответа, но есть http-агент clojure.contrib, который создает каждый запрос на получение/отправку в качестве своего собственного агента. Таким образом, вы можете запустить тысячу запросов, и все они будут выполняться параллельно и завершены по мере поступления результатов.
Ответ 4
Глядя на работу pmap, кажется, идет 32 потока за один раз, какое количество процессоров у вас есть, проблема в том, что карта будет идти впереди вычислений на 32, а фьючерсы начинаются сами по себе. (ОБРАЗЕЦ)
(defn samplef [n]
(println "starting " n)
(Thread/sleep 10000)
n)
(def result (pmap samplef (range 0 100)))
; вы будете ждать 10 секунд и увидите 32 отпечатка, тогда когда вы возьмете 33-ю другую 32 ; печатает эти минуты, что вы одновременно выполняете 32 параллельных потока ; мне это не идеально ; САЛУДОС Фелипе