foreachパッケージで、並列化する/しないを綺麗に書き分ける
で、「これからRで並列化するならforeachパッケージ使うだろ」という話を書いたが、その続き的な?もの。
並列化する/しないを、どう美しく書き分けるにはどうしたらよいかというお話。
たとえば、自作の関数かなんかで、並列化する/しないを書き分けようとすると、引数かなんかにparallel引数を用意しておいて、以下のように書いてしまうんじゃかろうか。
parallel=TRUE if(parallel){ foreach(i=1:3) %dopar% { rnorm(i) } }else{ foreach(i=1:3) %do% { rnorm(i) } }
これはdo/dopar部分だけが違うだけであって、冗長なコードだ。Rだと、最近流行りのパイプ一族系ポスト
を見てもわかるように、%で挟まれた奴らは実はこれ関数なのです。なので、以下のように書くことで、上のコードは
parallel=TRUE doop <- ifelse(parallel, `%do%`, `%dopar%`) doop(foreach(i=1:3), {rnorm(i)})
とまで簡略化される。こっちのほうがスマートに(私には)見えるので、これでいく。ここのコードではワーカーの立ち上げ・停止は書いていないことに注意。立ち上げたワーカー(スレッド、というかRScript.exeのプロセス)を正しく停止させるためには以下を見ておくとよい。
これを受けて、pipeR三銃士
そういえば前回の懇親会ほくそLTの時に、@teramonagi @hoxo_m はpipeR三銃士とか呼ばれてました
— 十二指腸、あるいは犬の夢 (@dichika) 2014, 9月 23
である俺はpipeRパッケージを活用して以下のように書いておくことにした。
library(pipeR) parallel <- TRUE doop <- if(parallel){detectCores() %>>% (cl = makeCluster(.)) %>>% registerDoParallel; `%dopar%`}else{`%do%`} doop(foreach(i=1:3), {rnorm(i)}) if(parallel){stopCluster(cl)}
さようなら可読性。そしてさらにコメント欄で貰ったアドバイスをもとにすると
library(pipeR) parallel <- TRUE "%doop%" <- if(parallel){detectCores() %>>% (cl = makeCluster(.)) %>>% registerDoParallel; `%dopar%`}else{`%do%`} foreach(i=1:3) %doop% { rnorm(i) } if(parallel){stopCluster(cl)}
とできる。これがスマートかなぁ。