并行包在另一个R包中使用时初始化非常慢
出于测试目的,我在我的R包中放置了以下函数:并行包在另一个R包中使用时初始化非常慢,r,parallel-processing,R,Parallel Processing,出于测试目的,我在我的R包中放置了以下函数: parsetup <- function(){ cl <- parallel::makeCluster(12,type='PSOCK') parallel::clusterCall(cl,function() 1+1) } 我认为有两件事在起作用。 第一个是。 根据功能的复杂性, 可能需要将大量数据发送到并行进程。 以创建一些闭包的代码为例: cl <- parallel::makeCluster(2L) fun_fac
parsetup <- function(){
cl <- parallel::makeCluster(12,type='PSOCK')
parallel::clusterCall(cl,function() 1+1)
}
我认为有两件事在起作用。 第一个是。 根据功能的复杂性, 可能需要将大量数据发送到并行进程。 以创建一些闭包的代码为例:
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"
在包中定义的函数有几个环境。
我怀疑每件事都会被连载到工人中进行评估,
但我并不惊讶这一点不容忽视。
此外,当您执行需要Worker中的其他包的函数时,
每个工作人员必须加载要执行的包,
因此,您不想一直重新创建cl
一些开销是不可避免的,
但为了避免一直重新创建“集群”,
您可以将其管理责任转交给用户,
对于包开发人员来说,这可能会简化一些事情,因为您不必担心调用parallel::stopCluster
。
我个人喜欢书中的抽象概念。
在软件包中,您可以定义如下函数:
my_par_fun <- function(x) {
foreach::foreach(x = x) %dopar% {
x + 1
}
}
在调用包的函数之前。
完成后,可以调用parallel::stopCluster
和foreach::registerDoSEQ
,
所有这些对您的包代码都是透明的
仅供参考,在使用foreach时,
您不需要使用数据,
你可以这样做:
my_par_fun <- function(...) {
foreach::foreach(i = 1L:foreach::getDoParWorkers()) %dopar% {
# a very time-consuming task
}
}
my_par_'u fun奇数。以下是一些故障排除建议:(i)添加一个print(cl)
,以确保您确实获得了您期望的集群节点数。(ii)添加print(system.time)(cl R 4.0.4,但我在R 4.0之前也遇到过此类问题。时间差发生在clusterCall中。如果我之后再进行一次clusterCall,速度会非常快,使我认为这是某种初始设置,好像节点正在复制包中的所有内容或其他内容。添加了sessionInfo()要发布。您也可以尝试创建一个只包含上述代码的框架包,以查看它是否更改了内容。在一个新的空包中,时间很好。在一个比我小的任意不同包(不是我的)中,从github克隆,仍然需要3.5秒。知道它是clusterCall()
是一个很好的线索。如果用parallel::clusterEvalQ(cl,1+1)替换它
,它仍然很慢吗?我仍然不明白的是,为什么我似乎不能通过改变函数环境和在不同环境中进行评估来解决这种行为…@Charlie如果我在全局环境中创建cl
,然后运行clusterCall(cl,package::fun),我不确定你是否可以避免它
,我可以看到(使用clusterEvalQ(cl,loadedNamespaces())
)加载了包及其所有依赖项,即使fun
没有显式地对它们做任何操作。
my_par_fun <- function(x) {
foreach::foreach(x = x) %dopar% {
x + 1
}
}
cl <- parallel::makeCluster(2L)
doParallel::registerDoParallel(cl)
my_par_fun <- function(...) {
foreach::foreach(i = 1L:foreach::getDoParWorkers()) %dopar% {
# a very time-consuming task
}
}