パッケージ関数を並行して使用するようにワーカーを初期化する方法
私はRパッケージを開発していて、驚異的並列問題のために並列処理を使用しようとしています。パッケージの他の関数を使用するループまたは関数を記述したいと思います。Windowsで作業していて、とを使用parallel::parLapply
してみましたforeach::%dopar%
が、ワーカー(コア)にパッケージ内の関数にアクセスさせることができません。これは、2つの関数を持つ単純なパッケージの例です。2番目の関数は、次を使用して並列ループ内で最初の関数を呼び出します%dopar%
。
add10 <- function(x) x + 10
slowadd <- function(m) {
cl <- parallel::makeCluster(parallel::detectCores() - 1)
doParallel::registerDoParallel(cl)
`%dopar%` <- foreach::`%dopar%` # so %dopar% doesn't need to be attached
foreach::foreach(i = 1:m) %dopar% {
Sys.sleep(1)
add10(i)
}
stopCluster(cl)
}
でパッケージをロードしdevtools::load_all()
てslowadd
関数を呼び出すと、Error in { : task 1 failed - "could not find function "add10""
が返されます。
また、パッケージを使用してワーカーを明示的に初期化しようとしました。
add10 <- function(x) x + 10
slowadd <- function(m) {
cl <- parallel::makeCluster(parallel::detectCores() - 1)
doParallel::registerDoParallel(cl)
`%dopar%` <- foreach::`%dopar%` # so %dopar% doesn't need to be attached
foreach::foreach(i = 1:m, .packages = 'mypackage') %dopar% {
Sys.sleep(1)
add10(i)
}
stopCluster(cl)
}
しかし、エラーが発生します。Error in e$fun(obj, substitute(ex), parent.frame(), e$data) : worker initialization failed: there is no package called 'mypackage'
パッケージ内の関数にワーカーがアクセスできるようにするにはどうすればよいですか?を使用したソリューションforeach
は素晴らしいですが、私はparLapply
または他の関数/パッケージを使用したソリューションに完全にオープンです。
回答
人々の有益なコメントのおかげで、パッケージの関数でワーカーを初期化することができました。必要なすべてのパッケージ関数がNAMESPACEにエクスポートされていることを確認し、でパッケージをインストールすることによりdevtools::install()
、foreach
初期化するパッケージを見つけることができました。この例のRスクリプトは次のようになります。
#' @export
add10 <- function(x) x + 10
#' @export
slowadd <- function(m) {
cl <- parallel::makeCluster(parallel::detectCores() - 1)
doParallel::registerDoParallel(cl)
`%dopar%` <- foreach::`%dopar%` # so %dopar% doesn't need to be attached
out <- foreach::foreach(i = 1:m, .packages = 'mypackage') %dopar% {
Sys.sleep(1)
add10(i)
}
stopCluster(cl)
return(out)
}
これは機能していますが、理想的なソリューションではありません。まず、ワークフローがはるかに遅くなります。devtools::load_all()
パッケージを変更するたびに使用していて、(並列処理を組み込む前に)テストしたかったのですが、今では毎回パッケージを再インストールする必要があり、パッケージが大きいと時間がかかります。次に、並列ループで必要なすべての関数をエクスポートして、それforeach
を見つけることができるようにする必要があります。私の実際のユースケースには、内部に保持したい小さなユーティリティ関数がたくさんあります。