snowパッケージを使って、並列処理をやってみた

卒研のプログラムも最後の段階ではかなり時間がかかるものがあった。Cで高速化したけど、それでも12時間くらいかかるというものもあった。どうにかやってもうちょっと早くしたいということで最後の手段(、並列処理に手を出してみることにした。

並列処理と(同期しない)マルチスレッドがどう違うかもよく分かっていないけど、snowパッケージというもので並列処理ができるらしい。並列処理なので、例えばMCMCのように前の結果が現在の状態に影響して…というのはたぶん早くならないと思うし、できるのかよく分からない。まあ、そういうわけで、並列処理やったからといってなんでも早くできるというわけではないようだ。

今回、私がやってみたのは

  • x_1,\cdots,x_nを[0,10]区間からの一様乱数とする
  • x_1,\cdots,x_nに指定した\betaをかけて、正規乱数を加えたものをy_1,\cdots,y_nとする
  • \betaの推定を線形回帰で行なう

というのを一回の処理として、これをN回行なう。そうすれば回帰係数\betaの推定値のサンプルがN個集まる、という感じの処理である。id:syou6162の卒研の発表を聞いていた人ならば、ぴんときたかもしれないけど、最後のクロスバリデーションで回した時の処理と似ている形をわざと選んでいる。

この中での一回の処理は互いに影響しあうことがないので、並列処理がうまくいけば計算時間が半減くらいにできる予定である。というわけでやってみた。まず、パッケージのインストールとロード。

install.packages("snow")
library(snow)

次にクラスタを生成する。マニュアルによれば、3種類方法があるとのことだけど、ここでは一番簡単なソケットを使った方法でやってみることにする。

cl <- makeSOCKcluster(c("localhost", "localhost"))

似たようなことをこんな感じでもできるようである。

cl <- makeCluster(2, type = "SOCK")

次に適当に、さっき上に書いた回帰係数を計算させる関数をでっちあげる。

make.beta <- function(beta,n){
  x <- runif(n,0,10)
  y <- beta * x + rnorm(n,0,1)
  beta <- lm(y~x)$coef[2]
  names(beta) <- NULL
  return(beta)
}

で、これを走らせたいんだが、自分で作った関数はどうやらクラスタのほうに教えてあげる必要があるようだ。

clusterExport(cl, "make.beta")

で、並列じゃない普通のsapplyと並列版のsapplyであるparSapplyとの時間の比較をする。

> system.time(sapply(1:1000,function(x){make.beta(3,100)}))
   ユーザ   システム       経過  
     2.993      0.019      3.044 
> system.time(parSapply(cl, 1:1000,function(x){make.beta(3,100)}))
   ユーザ   システム       経過  
     0.004      0.001      1.729 

倍まではいかないけど、大分早くなるな。ちなみに環境はこんな感じ。Core2Duoです。

> sessionInfo()
R version 2.8.0 (2008-10-20) 
i386-apple-darwin9.5.0 

locale:
ja_JP.UTF-8/ja_JP.UTF-8/C/C/ja_JP.UTF-8/ja_JP.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] snow_0.3-3

どうでもいいんだけど

cl <- makeCluster(100, type = "SOCK")

とかやっても2倍程度までしか早くなりません。今度先輩のquad走らせてみて1/4くらいの時間でできるか試してみようかな。