gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-03 18:49:31 25 4
gpt4 key购买 nike

出于测试目的,在我的 R 包中,我放置了以下函数:

parsetup <- function(){
cl <- parallel::makeCluster(12,type='PSOCK')
parallel::clusterCall(cl,function() 1+1)
}
当我运行 mypkg::parsetup() ,大约需要 6 秒才能完成。
当我运行 parsetup2 <- mypkg:parsetup(); parsetup2()在全局环境中,大约需要 6s 才能完成。
当我在全局环境中运行定义解析函数的代码,然后运行 ​​ parsetup() , 需要 ~ 0.3s
这对我来说似乎很愚蠢,任何人都可以解释原因和/或提出解决方法吗?在我想使用并行化的每个函数中添加 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
profvis

最佳答案

我认为这里有几件事在起作用。
第一个是environments and their parents .
根据函数的复杂程度,
可能必须将大量数据发送到并行进程。
以这个创建一些闭包的代码为例:

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"
如您所见,该函数“包含”在包含 x 的环境中。 ,
当函数被发送给工作人员进行评估时,
这种环境需要“复制”,
这会产生 x 的副本具有不同的内存地址。
您看不到与 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"
包内定义的函数有一些环境 "surrounding" them .
我怀疑一切都会被序列化以供 worker 评估,
但我并不惊讶它不可忽略。
此外,当您在工作人员中执行需要其他包的功能时,
每个 worker 必须加载包以执行,
所以你不想重新创建 cl每时每刻。
一些开销是不可避免的,
但是为了避免一直重新创建“集群”,
你云将其管理的责任转嫁给用户,
作为包开发人员,这可能会简化一些事情,因为您不必担心调用 parallel::stopCluster .
我个人喜欢 foreach 中的抽象。 .
在您的包中,您定义如下功能:
my_par_fun <- function(x) {
foreach::foreach(x = x) %dopar% {
x + 1
}
}
如果没有注册并行后端,代码将按顺序执行。
如果用户想要并行化,
他们可以安装后端包,如 doParallel 并调用类似的东西
cl <- parallel::makeCluster(2L)
doParallel::registerDoParallel(cl)
在调用你的包的函数之前。
完成后,他们可以调用 parallel::stopClusterforeach::registerDoSEQ ,
这一切都对您的包代码保持透明。
仅供引用,使用 foreach 时,
你不需要消耗数据,
您可以执行以下操作:
my_par_fun <- function(...) {
foreach::foreach(i = 1L:foreach::getDoParWorkers()) %dopar% {
# a very time-consuming task
}
}
这样每个 worker 都有一个任务,
不管用户创建了多少。

关于r - 并行包在另一个 R 包中使用时初始化非常慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67163535/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com