Rでお手軽パラレル・コンピューティング

  • ある処理をする関数 FUN( ) があるとする
  • 処理 FUN( ) に渡したい入力データセットがいくつもあって、その一つ一つがlistオブジェクト X になっているとする
  • コンピュータがLINUX(またはMac)のとき
  • parallel パッケージを使って
library(parallel)
out <- mclapply(X,FUN)
  • とするだけでよい
  • こうすることで、mclapply( ) 関数が、リストX の要素を次のようなルールで処理して、返してくる(こちらのソースを読めば解る)
    • もし、リストX の長さ(処理したい入力データセット数)がコンピュータのコア数以下のときは、個々のデータセットをそれぞれ異なるコアに処理させて、その結果が全部終われば、結果が返ってくる
    • もし、リストX の長さがコンピュータのコア数より大きいときには、すべてのコアにまずは最初の仕事を割り振り、処理の終わったコアに、未処理の仕事を割り付けることを続ける
  • 大小さまざまな行列が入力データであるときに、行列を使った計算をさせるという仕事をさせてみる
library(parallel)

# リストを作る(様々なサイズの整数行列)
n.dataset <- 10000
X <- list()
for(i in 1:n.dataset){
  d <- sample(2:1000,1)
  X[[i]] <- matrix(sample(1:10000,d^2,replace=TRUE),d,d)
}

# これの逆行列を何度も計算して、そのdeterminantを計算するというタスクをさせることにする
my.FUN <- function(M){
  for(i in 1:1000){
    Minv <- solve(M)
  }
  return(det(Minv))
}

# コア数1
start_time <- Sys.time()
out1 <-mclapply(1:n.dataset,my.FUN,mc.cores=1)
end_time <- Sys.time()
print(end_time-start_time)

# コア数2(mc.coresを指定しないと、PCのコア数を指定する仕様になっている)
start_time <- Sys.time()
out2 <-mclapply(1:n.dataset,my.FUN,mc.cores=2)
end_time <- Sys.time()
print(end_time-start_time)

# コア数2でmc.preschedule = FALSE
start_time <- Sys.time()
out3 <-mclapply(1:n.dataset,my.FUN,mc.cores=2,mc.preschedule=FALSE)
end_time <- Sys.time()
print(end_time-start_time)
  • 結果、コア数2のときは単純に時間が半分になっている
> # コア数1
> start_time <- Sys.time()
> out1 <-mclapply(1:n.dataset,my.FUN,mc.cores=1)
> end_time <- Sys.time()
> print(end_time-start_time)
Time difference of 14.49073 secs
> 
> # コア数2(mc.coresを指定しないと、PCのコア数を指定する仕様になっている)
> start_time <- Sys.time()
> out2 <-mclapply(1:n.dataset,my.FUN,mc.cores=2)
> end_time <- Sys.time()
> print(end_time-start_time)
Time difference of 8.580922 secs
> 
> # コア数2でmc.preschedule = FALSE
> start_time <- Sys.time()
> out3 <-mclapply(1:n.dataset,my.FUN,mc.cores=2,mc.preschedule=FALSE)
> end_time <- Sys.time()
> print(end_time-start_time)
Time difference of 29.38319 secs
    • なお、mc.prescheduleというオプションがあって、データセット数がそれほど多くなく、データセット間のタスクの重さにばらつきが大きいときは、mc.preschedule=FALSEを選ぶのがよいらしいが、上の例では、ばらつきは大きいものの、データセット数が多いので、ぜんぜん速くなっていない
  • ちなみにWindowsの場合には、mclapply()によるパラレル処理はできない。それは、Windowsでparallelパッケージを導入して、mclapply()関数の中身を見てみると、単に、lapply()関数を(1コアで)回す仕様になっていることからも解る
# Windowsでのparallel::mclapply()
> mclapply
function (X, FUN, ..., mc.preschedule = TRUE, mc.set.seed = TRUE, 
    mc.silent = FALSE, mc.cores = 1L, mc.cleanup = TRUE, mc.allow.recursive = TRUE) 
{
    cores <- as.integer(mc.cores)
    if (cores < 1L) 
        stop("'mc.cores' must be >= 1")
    if (cores > 1L) 
        stop("'mc.cores' > 1 is not supported on Windows")
    lapply(X, FUN, ...)
}
<bytecode: 0x00000000234a5c70>
<environment: namespace:parallel>