pvaluesとか

clojureは並行処理を得意としているので各処理が独立してできる場合、並行で処理をさせると高速化できる場合があります。代表的な関数としてはpmap、pvalues、pcallなど。

pmap

vectorの各要素に関して、ある処理を回したいとかの場合、pmapがよいでしょう。たぶん一番これがよく使う。

user=> (pmap #(+ 1 %) [1 2 3 4 5])
(2 3 4 5 6)

ここでは+1するだけの軽い処理ですが、pmapをするような場合はある程度重い関数でないとオーバーヘッドのほうが大きくなってしまいます。そういう場合はよくsequenceをpartitionしてからpmapし、flattenするというのがよくやられます(たぶん)。元々のseqがネストしてたりするとflattenした場合予想以上にflattenされてくるので、注意が必要。

(def N 10000)
(def long-seq (range N))
(flatten (pmap #(map + %) (partition (/ N 5) long-seq)))

この場合は5分割してやっている例。使えるコア数とかに応じて変えるとよい。

pvalues

並行で処理したいのがvectorの要素、とかではなく、いくつかの式を並行でやりたい場合に使う。こんな感じ。

(pvalues (heave-calc1 x y) (heave-calc2 x y) (heave-calc3 x y))

pcalls

引数と取らない関数を並行で処理させたい場合に使う。こんな感じ。

(pcall function-1 function-2 ...)

pvaluesの中身を見るとpcallsで定義されているのが分かる。

(defmacro pvalues
  "Returns a lazy sequence of the values of the exprs, which are
  evaluated in parallel"
  {:added "1.0"
   :static true}
  [& exprs]
  `(pcalls ~@(map #(list `fn [] %) exprs)))