foreach+doSNOWパッケージを使って、並列処理をやってみた
なんだかweb屋界隈ではHadoopだのMahoutだの象を使うだの使わないだの楽しそうな事をやっていて、私はとてもさみしく、そしてうらやましくもなったわけですが手元にそんな立派な環境なんてないわけで。しかし、そんな私にもマルチコアのパソコンが与えられているのでそれで計算の並列化をやってみた。OSはwindows XP(32bit)。
使うのは統計数理研究所のセミナーでお薦めされていたforeachパッケージ*1。これはRevlution Rの人たちが作っているので今後とも継続的な開発と進化が期待できるとのこと。まずは、doSNOWパッケージも合わせてインストール。
install.packages("foreach") install.packages("doSNOW")
foreachパッケージではforループの並列化をしてくれるのですが、処理を並列化させないで書くこともできるのでまずはそっちの基本的な使い方を書く。
library(foreach) #基本系(返却値リスト) foreach(i = 1:3) %do% {sqrt(i)} #.combineオプションを指定することで返却値をベクトルにすることもできる foreach(i = 1:3,.combine = "c") %do% {sqrt(i)} #{}内の返却値自体がベクトルの時は.combineにcbindを指定して行列にもできる foreach(i = 1:3,.combine = "cbind") %do% {letters[1:4]} #自分で定義した結合方式も可能(↑の.combine関数にcを指定したケースと同じになるようにしてみた) MyFunc <- function(x,y)c(x,y) foreach(i = 1:3, .combine = "MyFunc") %do% { sqrt(i) }
ここでMyFunc関数を定義するときは頭の中でReduce関数と同じ挙動をするもんだと考えるとやりやすい。
> Reduce(function(x,y)c(x,y),sqrt(1:3)) [1] 1.000000 1.414214 1.732051
こんな感じ。ちなみにforeachの引数は複数にもできる。
foreach(a = 1:1000, b = rep(10, 2)) %do% {a + b}
ただし、ループ回数は要素数の少ない方に合わせられる。この場合だとbの要素数が2なので2回計算して終わる。aのデータは初めの2要素しか使われない。
次に上の処理を並列にするために、doSNOWライブラリを読み込んでクラスタを作成する。私のマシンはDual Coreなんでクラスタ数は2を指定してます。
> library(doSNOW) > getDoParWorkers() [1] 1 > getDoParName() NULL > registerDoSNOW(makeCluster(2, type = "SOCK")) > getDoParWorkers() [1] 2 > getDoParName() [1] "doSNOW" > getDoParVersion() [1] "1.0.3"
これで準備ができたので計算させる。並列化させるためには%do%を%dopar%に変えるだけでOK.
> N <- 10^4 > system.time(foreach(i = 1:N,.combine = "cbind") %do% { + sum(rnorm(N)) + }) ユーザ システム 経過 57.52 0.48 59.60 > system.time(foreach(i = 1:N,.combine = "cbind") %dopar% { + sum(rnorm(N)) + }) ユーザ システム 経過 18.61 0.58 37.74
まぁ倍とは言えないけどそれなりに早くなったかなと。これでforeachパッケージを用いた並列化が意外と簡単にできることがわかった。doSNOWパッケージ以外にもクラスタを作るためのdoMPIパッケージなんてのもあったりするので試してみるといいかも。ちなみに参考サイトにあるUsing The foreach Packageに書いてあるランダムフォレストの並列化とかはなかなか面白そう。
参考サイト(PDF)
*1:ちなみにsnowパッケージでの並列化の話はsnowパッケージを使って、並列処理をやってみた - yasuhisa's blogでid:syou6162さんが書かれている