R Academy · レッスン

parallel パッケージと detectCores()

fork クラスターまたはソケットクラスターを起動し、CPU コア間で処理を分散します。

レッスン 1/413 ステップ

「parallel パッケージと detectCores()」はCoddyKit上の無料R Academyレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはR Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 R Academyコースには全4レッスンが含まれています。

並列計算を行う理由

現代のコンピューターには複数のCPUコアがあります。Rはデフォルトでは1つのコアで実行されるため、残りのコアはアイドル状態になります。Rに組み込まれているparallelパッケージを使うと、すべてのコアを活用して反復的な計算を高速化できます。

# Check how many cores your machine has
library(parallel)

total_cores <- detectCores()
logical_cores <- detectCores(logical = TRUE)
physical_cores <- detectCores(logical = FALSE)

cat('Total logical cores:', total_cores, '
')
cat('Physical cores:', physical_cores, '
')

makeClusterとstopCluster

makeCluster(n)はn個のワーカープロセスを起動します。リソースを解放するため、処理が終わったら必ずstopCluster(cl)を呼び出します。OS用に1つのコアを残すため、detectCores() - 1を使うのが一般的です。

library(parallel)

# Spawn workers (leave 1 core for system)
n_cores <- detectCores() - 1
cl <- makeCluster(n_cores)

cat('Cluster created with', n_cores, 'workers\n')

# Always clean up!
stopCluster(cl)
cat('Cluster stopped.\n')

clusterExport:変数の共有

ワーカープロセスはそれぞれ独自のメモリ空間を持つため、グローバル環境を参照できません。clusterExport(cl, varlist)を使用すると、名前を指定したオブジェクトをマスターからすべてのワーカーへコピーできます。

library(parallel)
cl <- makeCluster(2)

# Define a variable and a function in the master
base_value <- 100
add_base <- function(x) x + base_value

# Export them to workers
clusterExport(cl, varlist = c('base_value', 'add_base'))

# Now workers can use them
result <- parLapply(cl, 1:4, function(x) add_base(x))
cat(unlist(result), '\n')  # 101 102 103 104

stopCluster(cl)

clusterEvalQ:セットアップコードの実行

clusterEvalQ(cl, expr)は、すべてのワーカーで式を評価します。メインの計算を開始する前に、すべてのノードでパッケージを読み込んだり、ヘルパーファイルを読み込んだりする場合に便利です。

library(parallel)
cl <- makeCluster(2)

# Load a package on every worker
clusterEvalQ(cl, {
  library(stats)
  set.seed(42)  # set seed per worker
})

# Each worker can now use stats functions
result <- parLapply(cl, 1:4, function(n) rnorm(n, mean = 0, sd = 1))
result[[1]]  # one random normal value

stopCluster(cl)

parLapply:並列lapply

parLapply(cl, X, FUN)は、lapply()に相当する並列関数です。Xの要素をワーカーに分配し、結果をリストとして収集します。Windows、macOS、Linuxのすべてのプラットフォームで動作します。

library(parallel)
cl <- makeCluster(2)

# Simulate slow computation: sleep 0.1s per item
clusterEvalQ(cl, Sys.sleep)

slow_square <- function(x) {
  Sys.sleep(0.05)
  x^2
}

clusterExport(cl, 'slow_square')

system.time(
  result <- parLapply(cl, 1:8, slow_square)
)
cat(unlist(result), '\n')  # 1 4 9 16 25 36 49 64

stopCluster(cl)

parSapply:結果の簡略化

parSapply(cl, X, FUN)は、sapply()の並列版です。可能な場合は結果をベクトルまたは行列に自動的に簡略化するため、関数がスカラーを返す場合に便利です。

library(parallel)
cl <- makeCluster(2)

# parSapply returns a simplified vector
squares <- parSapply(cl, 1:10, function(x) x^2)
cat(squares, '\n')  # 1 4 9 16 25 36 49 64 81 100

# Returns a matrix if FUN returns a vector of same length
stats_result <- parSapply(cl, 1:4, function(n) {
  x <- rnorm(100)
  c(mean = mean(x), sd = sd(x))
})
print(stats_result)  # 2x4 matrix

stopCluster(cl)

mclapply:フォークベースの並列処理

mclapply()はフォークを使用します。新しいプロセスを起動する代わりに現在のRプロセスをコピーするため、起動が速く、ワーカーは親環境へ暗黙的にアクセスできます。ただし、Unix/macOSでのみ動作し、Windowsでは動作しません。

library(parallel)

# mclapply: Unix/macOS only
# Workers inherit the parent environment automatically
base_value <- 42

if (.Platform$OS.type != 'windows') {
  result <- mclapply(
    1:8,
    function(x) x * base_value,  # base_value visible without export
    mc.cores = 4
  )
  cat(unlist(result), '\n')
} else {
  cat('mclapply not supported on Windows. Use parLapply instead.\n')
}

逐次処理と並列処理のベンチマーク

並列処理には、クラスターの起動、データのシリアライズ、プロセス間通信などのオーバーヘッドがあります。要素ごとの計算が十分に高コストで、このオーバーヘッドを上回る場合にのみ効果を発揮します。

library(parallel)
cl <- makeCluster(2)

# Task: compute 100 iterations of matrix multiply
heavy_task <- function(n) {
  m <- matrix(rnorm(200), nrow = 100)
  sum(m %*% t(m))
}
clusterExport(cl, 'heavy_task')

seq_time <- system.time(lapply(1:20, heavy_task))[['elapsed']]
par_time <- system.time(parLapply(cl, 1:20, heavy_task))[['elapsed']]

cat('Sequential:', round(seq_time, 3), 's\n')
cat('Parallel:  ', round(par_time, 3), 's\n')
cat('Speedup:   ', round(seq_time / par_time, 2), 'x\n')

stopCluster(cl)

並列処理でのRNGのシード設定

並列処理での乱数生成は扱いが難しく、各ワーカーに独立した再現可能なストリームが必要です。clusterSetRNGStream(cl, seed)をL'Ecuyer-CMRGジェネレーターとともに使用すると、再現可能な並列乱数を生成できます。

library(parallel)
cl <- makeCluster(2)

# Set reproducible RNG streams across workers
clusterSetRNGStream(cl, iseed = 123)

# Each worker uses its own independent random stream
results1 <- parSapply(cl, 1:6, function(i) rnorm(1))

# Reset and repeat — same results
clusterSetRNGStream(cl, iseed = 123)
results2 <- parSapply(cl, 1:6, function(i) rnorm(1))

cat('Run 1:', round(results1, 4), '\n')
cat('Run 2:', round(results2, 4), '\n')
cat('Identical:', identical(results1, results2), '\n')

stopCluster(cl)

クラスターでのエラー処理

ワーカーでエラーが発生すると、parLapply()はそのエラーをマスタープロセスで再送出します。関数内で呼び出しをtryCatch()で囲むか、parLapply()全体の呼び出しをtryCatch()で囲んで、エラーを適切に処理します。

library(parallel)
cl <- makeCluster(2)

# Wrap risky code inside the worker function
safe_log <- function(x) {
  tryCatch(
    log(x),
    warning = function(w) NA_real_,
    error   = function(e) NA_real_
  )
}
clusterExport(cl, 'safe_log')

# -1 produces NaN warning, 'a' produces an error
input <- list(4, 9, -1, 'a', 16)
result <- parLapply(cl, input, safe_log)
cat(unlist(result), '\n')  # 1.386 2.197 NaN NA 2.773

stopCluster(cl)

実践的な並列処理パターン

これは、コア数の検出、クラスターの作成、依存関係のエクスポート、計算の実行、結果の収集、クラスターの確実な停止までを行う、完全で慣用的な並列処理のワークフローです。エラーが発生した場合でも、on.exit()を使用して必ずクラスターを停止します。

library(parallel)

run_parallel <- function(data, fn, n_workers = detectCores() - 1) {
  cl <- makeCluster(n_workers)
  on.exit(stopCluster(cl))  # guaranteed cleanup

  clusterExport(cl, 'fn', envir = environment())

  result <- parLapply(cl, data, fn)
  result
}

# Example: bootstrap mean estimation
samples <- lapply(1:100, function(i) rnorm(50, mean = 5, sd = 2))
means <- run_parallel(samples, mean)

cat('Grand mean:', round(mean(unlist(means)), 3), '\n')
cat('95% CI: [',
    round(quantile(unlist(means), 0.025), 3), ',',
    round(quantile(unlist(means), 0.975), 3), ']\n')

クイックチェック

並列処理のコード内でエラーが発生した場合でもstopCluster(cl)が呼び出されることを保証するには、どの関数を使用すべきですか?

まとめ:parallelパッケージ

重要なポイント:

  • detectCores()は利用可能なコア数を返します。クラスターのサイズにはdetectCores() - 1を使用します
  • makeCluster(n) / stopCluster(cl)でワーカーのライフサイクルを管理します
  • clusterExport()はオブジェクトをコピーし、clusterEvalQ()はワーカーでセットアップコードを実行します
  • parLapply() / parSapply()はクロスプラットフォーム対応の並列イテレーターです
  • mclapply()はUnix専用ですが、フォークにより起動が速くなります
  • 確実にクリーンアップするにはon.exit(stopCluster(cl))を使用します
  • 並列処理は、計算量の多いタスクでのみ効果を発揮します
# Minimal reproducible parallel pattern
library(parallel)

cl <- makeCluster(max(1, detectCores() - 1))
on.exit(stopCluster(cl))

clusterSetRNGStream(cl, iseed = 42)
result <- parSapply(cl, 1:8, function(x) x^2 + rnorm(1, 0, 0.1))
cat(round(result, 2), '\n')
無料で開始

AI チューターと学ぶ R — 無料

ブラウザでリアルコードを書いて実行し、24/7 の AI チューターから瞬時にサポートを受け、ウェブまたはアプリで続きから学習できます。

コース
43
レッスン
159

よくある質問

「parallel パッケージと detectCores()」レッスンは無料ですか?

はい。「parallel パッケージと detectCores()」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、R Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 R Academyコースには全4レッスンが含まれています。

「parallel パッケージと detectCores()」で何を学びますか?

fork クラスターまたはソケットクラスターを起動し、CPU コア間で処理を分散します。 ブラウザで直接実行するハンズオンコードでR Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

R Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのR Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。

「parallel パッケージと detectCores()」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このR Academyレッスンでコードを書いて実行できますか?

はい。すべてのR Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. parallel パッケージと detectCores()
  2. future フレームワーク
  3. furrr:purrr 操作の並列化
  4. 並列コードのデバッグと負荷分散
← R Academyに戻る