4

我正在开发一个 R 包并尝试在其中使用并行处理来解决令人尴尬的并行问题。我想编写一个循环或函数来使用我包中的其他函数。我在 Windows 中工作,我尝试使用parallel::parLapplyand foreach::%dopar%,但无法让工作人员(核心)访问我的包中的功能。这是一个包含两个函数的简单包的示例,其中第二个函数在并行循环中调用第一个函数%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或其他功能/包的解决方案完全开放。

4

1 回答 1

1

由于人们的有用评论,我能够使用我的包的功能初始化工作人员。通过确保在 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可以找到它。我的实际用例有很多小的实用功能,我宁愿保留在内部。

于 2020-08-22T18:45:14.917 回答