使R包中的函数可并行化的最佳实践是什么?

C8H*_*4O2 5 parallel-processing r parallel-foreach r-package

我开发了一个包含令人难以置信的并行功能的R包.

我希望以对用户透明的方式实现这些功能的并行化,而不管他/她的OS(至少理想情况下).

我环顾四周看看其他软件包作者是如何导入基于foreach的Parallelism的.例如,Max Kuhn的caret包导入foreach要使用,%dopar%依赖 于用户指定并行后端.(使用了几个示例doMC,这在Windows上不起作用.)

注意doParallel适用于Windows和Linux/OSX并使用内置parallel包(请参阅此处的注释以进行有用的讨论),导入doParallelregisterDoParallel()在用户指定parallel=TRUE为参数时调用函数是否有意义?

Ste*_*ton 6

我认为允许用户注册自己的并行后端非常重要.在doParallel后端是非常便携,但如果他们想在一个集群中多个节点上运行的功能是什么?如果他们想设置makeCluster"outfile"选项怎么办?如果将并行支持透明化也会使许多用户无法使用,这很不幸.

我建议您使用该getDoParRegistered函数来查看用户是否已经注册了并行后端,如果没有,则只为其注册一个.

这是一个例子:

library(doParallel)
parfun <- function(n=10, parallel=FALSE,
                   cores=getOption('mc.cores', 2L)) {
  if (parallel) {
    # honor registration made by user, and only create and register
    # our own cluster object once
    if (! getDoParRegistered()) {
      cl <- makePSOCKcluster(cores)
      registerDoParallel(cl)
      message('Registered doParallel with ',
              cores, ' workers')
    } else {
      message('Using ', getDoParName(), ' with ',
              getDoParWorkers(), ' workers')
    }
    `%d%` <- `%dopar%`
  } else {
    message('Executing parfun sequentially')
    `%d%` <- `%do%`
  }

  foreach(i=seq_len(n), .combine='c') %d% {
    Sys.sleep(1)
    i
  }
}
Run Code Online (Sandbox Code Playgroud)

编写这样parallel=TRUE,即使它们注册了并行后端,它也只能并行运行:

> parfun()
Executing parfun sequentially
 [1]  1  2  3  4  5  6  7  8  9 10
Run Code Online (Sandbox Code Playgroud)

如果parallel=TRUE并且他们没有注册后端,那么它将为它们创建并注册一个集群对象:

> parfun(parallel=TRUE, cores=3)
Registered doParallel with 3 workers
 [1]  1  2  3  4  5  6  7  8  9 10
Run Code Online (Sandbox Code Playgroud)

如果再次parfun调用parallel=TRUE,它将使用以前注册的集群:

> parfun(parallel=TRUE)
Using doParallelSNOW with 3 workers
 [1]  1  2  3  4  5  6  7  8  9 10
Run Code Online (Sandbox Code Playgroud)

这可以通过多种方式进行改进:它只是一个简单的演示.但至少它提供了一种便利,而不会阻止用户使用在其环境中可能需要的自定义选项注册不同的后端.


请注意,选择默认数量的核心/工作人员也是一个棘手的问题,而且是CRAN维护人员关心的问题.这就是我没有制作默认核心数的原因detectCores().相反,我正在使用所使用的方法mclapply,尽管可能应该使用不同的选项名称.


关于 stopCluster

请注意,此示例有时会创建一个新的群集对象,但它永远不会通过调用来停止它stopCluster.原因是创建集群对象可能很昂贵,所以我喜欢将它们重用于多个foreach循环,而不是每次都创建和销毁它们.我宁愿把它留给用户,但是,在这个例子中,用户没有办法做到这一点,因为他们无法访问cl变量.

有三种方法可以解决这个问题:

  • 通话stopClusterparfunmakePSOCKcluster被调用;
  • 编写一个附加函数,允许用户停止隐式创建的集群对象(相当于包中的stopImplicitCluster函数doParallel);
  • 不要担心隐式创建的集群对象.

我可能会为我自己的代码选择第二个选项,但这会使这个例子大大复杂化.它已经相当复杂了.


Hen*_*ikB 5

作为future包的作者,推荐大家看看。future 包将所有并行的并行/集群功能统一到一个 API 中。

https://cran.r-project.org/package=future

它的设计是让您作为开发人员编写一次代码,然后用户决定后端,例如plan(multiprocess)plan(cluster, workers = c("n1", "n3", "remote.server.org"))等等。

如果用户可以使用 Slurm、TORQUE/PBS 和 SGE 等常见调度程序之一访问 HPC 集群,那么他们可以使用 future.BatchJobs 包,该包在 BatchJobs 之上实现了未来的 API,例如plan(batchjobs_slurm)。您的代码保持不变。(很快,batchtools 之上也会有 future.batchtools 包))。