foreach の .multicombine 引数について #rstatsj

R の `foreach()` には `.multicombine` 引数があります。

この `.multicombine` 引数について、

- [福島真太朗『Rによるハイパフォーマンスコンピューティング』](http://www.amazon.co.jp/gp/product/4883379353) 

によると、

> `.combine` 引数で与えた関数の引数が 3 個以上であるかどうかを表すフラグ。関数の引数が 3 個以上の場合、この引数を `TRUE` にしないと、最初の 2 つの引数のみが使用される。(デフォルト値は `FALSE`)

と書いてあります。

しかし、`foreach` 関数のヘルプには、次のように書いてあります。

> logical flag indicating whether the `.combine` function can accept more than two arguments. If an arbitrary `.combine` function is specified, by default, that function will always be called with two arguments. If it can take more than two arguments, then setting `.multicombine` to `TRUE` could improve the performance. The default value is `FALSE` unless the `.combine` function is `cbind`, `rbind`, or `c`, which are known to take more than two arguments. 

超訳してみると、

> `.multicombine` は `.combine` 引数によって指定された関数が 2 つ以上の引数を受け取れるかどうかを示す論理フラグです。`.combine` 引数になんらかの関数が指定されている場合、デフォルトでは、その関数には 2 つの引数しか渡されません。もし、`.combine` 引数によって指定された関数が、3 つ以上の引数を受け取ることができる場合、`.multicombine` 引数を `TRUE` に設定することで、パフォーマンスの向上が期待できます。`.multicombine` 引数のデフォルト値は `FALSE` ですが、`cbind`, `rbind`および `c` の 3 つの関数に対しては、3 つ以上の引数を持つことがあらかじめ分かっているため、デフォルト値は `TRUE` になります。

となり、

- `.multicombine` 引数はパフォーマンス向上のためにある
- デフォルト値は `FALSE` だが、`cbind`, `rbind`, `c` に対しては `TRUE`

ということがわかります。

というわけで、`.multicombine` を `TRUE` にすると、本当にパフォーマンスが向上するのか調べてみました。

題材は、同じく

- [福島真太朗『Rによるハイパフォーマンスコンピューティング』](http://www.amazon.co.jp/gp/product/4883379353) 

に出てくる Random Forest の並列化バージョンです(p.154-155)。

```r:R
library(doParallel)
library(randomForest)
library(kernlab)

data(spam)
cores <- 4

Execute <- function(.multicombine) {
  cl <- makePSOCKcluster(cores)
  registerDoParallel(cl)
  
  start <- Sys.time()
  fit.rf <- foreach(ntree=rep(250, cores), .combine=combine, .export="spam",
                    .packages="randomForest", .multicombine=.multicombine) %dopar% {
    randomForest(type ~ ., data = spam, ntree = ntree)
  }
  end <- Sys.time()
  
  stopCluster(cl)
  (end - start)
}
```

```:結果
> Execute(.multicombine=FALSE)
Time difference of 12.18954 secs
> Execute(.multicombine=TRUE)
Time difference of 11.90909 secs
```

ここで、`.combine` 引数に指定された `combine()` 関数は、`randomForest` オブジェクトを結合する関数ですが、これは 3 つ以上の引数を取ることができるので、`.multicombine` 可能です。

`.multicombine=FALSE` の場合と `.multicombine=TRUE` の場合を比較していますが、あまり変わりませんね。
それもそのはず、`.combine` は reduce フェーズに使う関数なので、reduce する数が少ないと差はでません。

というわけで、`ntree=rep(250, cores)` を `ntree=rep(25, cores * 100)` に増やしてやってみます。

```r:R
library(doParallel)
library(randomForest)
library(kernlab)

data(spam)
cores <- 4

Execute <- function(.multicombine) {
  cl <- makePSOCKcluster(cores)
  registerDoParallel(cl)
  
  start <- Sys.time()
  fit.rf <- foreach(ntree=rep(25, cores * 10), .combine=combine, .export="spam",
                    .packages="randomForest", .multicombine=.multicombine) %dopar% {
    randomForest(type ~ ., data = spam, ntree = ntree)
  }
  end <- Sys.time()
  
  stopCluster(cl)
  (end - start)
}
```

```:結果
> Execute(.multicombine=FALSE)
Time difference of 18.75828 secs
> Execute(.multicombine=TRUE)
Time difference of 13.57622 secs
```

おー、`.multicombine=TRUE` の方が速くなりましたね！

というわけで、`foreach` の `.multicombine` 引数を使ってパフォーマンスが向上することが確認できました。

以上です。

### 関連記事

- [foreach の registerDoSEQ() について](http://qiita.com/hoxo_m/items/aacdf912559412b2b61b)
- [R の foreach で並列処理するとき on.exit() が便利](http://qiita.com/hoxo_m/items/f08b015bc9897d98775d)
- [R の foreach で並列処理するとき、クラスタを implicit にする](http://qiita.com/hoxo_m/items/02b82d44a09752509dc3)
- [R の foreach を自分用にカスタマイズする](http://qiita.com/hoxo_m/items/f3a3a27a9aa2fa9f7813)