この関数のドキュメントではpmap
、Webを介してXMLフィードのコレクションをフェッチするようなものがどれほど効率的であるか疑問に思います。pmapが生成する同時フェッチ操作の数と、最大値はわかりません。
4 に答える
ソースを確認すると、次のように表示されます。
> (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つの作業を開始し、少し先を行くようにしますが、最大値は2+nである必要があります。
future
最終的には、無制限の数のスレッドをサポートするエージェントI/Oスレッドプールを使用します。作業が行われると成長し、スレッドが使用されていない場合は縮小します。
pmapがどのように機能するかを説明するAlexの優れた回答に基づいて、状況に対する私の提案を以下に示します。
(doall
(map
#(future (my-web-fetch-function %))
list-of-xml-feeds-to-fetch))
理論的根拠:
- ほとんどがネットワークIOでブロックされるため、できるだけ多くの作業を実行中に行う必要があります。
- Futureは、スレッドプールで処理されるように、リクエストごとに非同期の作業を開始します。Clojureにそれをインテリジェントに処理させることができます。
- マップ上のdoallは、完全なシーケンスの評価(つまり、すべてのリクエストの起動)を強制します。
- メインスレッドはすぐに先物の間接参照を開始できるため、個々の結果が戻ってきても進歩を続けることができます
長い応答を書く時間はありませんが、各get/postリクエストを独自のエージェントとして作成するclojure.contribhttp-agentがあります。したがって、1,000のリクエストを実行でき、結果が表示されると、それらはすべて並行して実行され、完了します。
pmapの操作を見ると、プロセッサの数に関係なく、一度に32スレッドになるようです。問題は、mapが32までに計算を進め、先物が独自に開始されることです。(サンプル)
(defn samplef [n]
(println "starting " n)
(Thread/sleep 10000)
n)
(def result (pmap samplef (range 0 100)))
; 10秒間待ってから、32枚のプリントが表示されます。その後、33枚目と32枚目が表示されます。一度に32の同時スレッドを実行しているこの分を出力します; 私にとって、これは完璧ではありません; SALUDOSフェリペ