7

我开发了一个包含令人尴尬的并行函数的 R 包。

我想以对用户透明的方式实现这些功能的并行化,而不管他/她的操作系统如何(至少理想情况下)。

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

注意到doParallel适用于 Windows 和 Linux/OSX 并使用内置parallel包(有关有用的讨论,请参阅此处的评论),每当用户指定为参数时导入doParallel并调用我的函数是否有意义?registerDoParallel()parallel=TRUE

4

2 回答 2

7

我认为允许用户注册自己的并行后端非常重要。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
  }
}

编写它以便它仅在 if 时并行运行parallel=TRUE,即使它们注册了并行后端:

> parfun()
Executing parfun sequentially
 [1]  1  2  3  4  5  6  7  8  9 10

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

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

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

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

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


请注意,选择默认数量的核心/工作人员也是一个棘手的问题,也是 CRAN 维护人员关心的问题。这就是为什么我没有设置默认数量的核心detectCores()。相反,我使用的是 使用的方法mclapply,尽管可能应该使用不同的选项名称。


关于stopCluster

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

有三种方法可以处理这个问题:

  • 随时呼入stopCluster;_parfunmakePSOCKcluster
  • 编写一个附加函数,允许用户停止隐式创建的集群对象(相当于包中的stopImplicitCluster函数doParallel);
  • 不要担心隐式创建的集群对象。

我可能会为我自己的代码选择第二个选项,但这会使这个示例变得非常复杂。已经相当复杂了。

于 2017-02-22T16:36:35.990 回答
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). 您的代码保持不变。(很快也会有 future.batchtools 包在 batchtools 之上))。

于 2017-03-06T08:48:22.580 回答