繁体   English   中英

并行 package 在另一个 R package 中使用时初始化非常慢

[英]parallel package very slow initialization when used inside another R package

出于测试目的,在我的 R package 中,我放置了以下 function:

parsetup <- function(){
  cl <- parallel::makeCluster(12,type='PSOCK')
  parallel::clusterCall(cl,function() 1+1)
}

当我运行mypkg::parsetup()时,大约需要 6 秒才能完成。 当我运行parsetup2 <- mypkg:parsetup(); parsetup2() parsetup2 <- mypkg:parsetup(); parsetup2()在全局环境中,大约需要 6 秒才能完成。 当我在全局环境中运行定义解析 function 的代码,然后运行parsetup()时,大约需要 0.3s

这对我来说似乎很愚蠢,任何人都可以解释原因和/或提出解决方法吗? 在我想使用并行化的每个 function 中添加 6s 非常令人沮丧。

编辑:在 clusterCall 期间发生时间差异,在每种情况下创建的集群节点数为 12。

sessionInfo()
R version 4.0.4 (2021-02-15)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 19042)

Matrix products: default

locale:
[1] LC_COLLATE=English_United States.1252  LC_CTYPE=English_United States.1252    LC_MONETARY=English_United States.1252
[4] LC_NUMERIC=C                           LC_TIME=English_United States.1252    
system code page: 65001

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] ctsem_3.4.3    testthat_3.0.2 profvis_0.3.7  Rcpp_1.0.6 

专业人士

我认为这里有几件事在起作用。 第一个是环境和他们的父母 根据 function 的复杂性,可能需要将大量数据发送到并行进程。 以这个创建一些闭包的代码为例:

cl <- parallel::makeCluster(2L)

fun_factory <- function(x) { function() { list(x = x, addr = lobstr::obj_addr(x)) } }
fun <- fun_factory(0)

lobstr::obj_addr(environment(fun)$x)
# [1] "0x557ad9605068"

str(parallel::clusterCall(cl, fun))
# List of 2
#  $ :List of 2
#   ..$ x   : num 0
#   ..$ addr: chr "0x564b16e37e68"
#  $ :List of 2
#   ..$ x   : num 0
#   ..$ addr: chr "0x55a5a9437e68"

如您所见, function 被“包含”在一个包含x的环境中,当 function 被发送给工作人员进行评估时,该环境需要“复制”,这会导致x的副本具有不同的 ZCDE18D7B49357F06

您看不到与 clusterEvalQ 完全相同的行为,因为clusterEvalQ仅序列化代码表达式(另请参见?base::quote ),因此在此示例中它不会直接工作:

#parallel::clusterExport(cl, "fun") # uncomment this to make it work
parallel::clusterEvalQ(cl, fun())
Error in checkForRemoteErrors(lapply(cl, recvResult)) : 
  2 nodes produced errors; first error: could not find function "fun"

包内定义的函数有一些 “围绕”它们的环境。 我怀疑所有内容都会被序列化以供工人进行评估,但我并不感到惊讶它并非可以忽略不计。 另外,当你在worker中执行需要其他包的函数时,每个worker都必须加载package来执行,所以你不想一直重新创建cl

一些开销是不可避免的,但为了避免一直重新创建“集群”,您将云管理的责任交给用户,对于 package 开发人员来说,这可能会简化一些事情,因为您不必担心调用parallel::stopCluster 我个人喜欢foreach中的抽象。 在您的 package 中,您可以定义如下函数:

my_par_fun <- function(x) {
    foreach::foreach(x = x) %dopar% {
        x + 1
    }
}

如果没有注册并行后端,代码将按顺序执行。 如果用户想要并行化,他们可以安装像doParallel这样的后端包并调用类似的东西

cl <- parallel::makeCluster(2L)
doParallel::registerDoParallel(cl)

调用你的包的函数之前。 完成后,他们可以调用parallel::stopClusterforeach::registerDoSEQ ,这对您的 package 代码保持透明。

仅供参考,在使用foreach时,您不需要使用数据,您可以执行以下操作:

my_par_fun <- function(...) {
    foreach::foreach(i = 1L:foreach::getDoParWorkers()) %dopar% {
        # a very time-consuming task
    }
}

这样,每个工作人员都会获得一项任务,而不管用户创建了多少。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM