首页 > 解决方案 > 我可以使用 for 循环的并行版本并一起应用系列吗?

问题描述

最近在做研究的时候遇到一个问题:一开始我定义了一个函数myfunction,里面有两个for循环,然后我用lapply(datalist, myfunction)了,但是处理速度太慢了。

然后我学习了两个并行包'foreach'和'parallel'来进行并行计算。所以我将两个进程都更改为它们的并行版本。

但是我发现当我运行我的代码时,我的函数中的 foreach 似乎不起作用。

myfunction <- function{data} {

   df  <- foreach (i = 1:200, .combine = "rbind") %:% 
    foreach(j = 1:200, .combine = "rbind") %dopar% {

      *****
      process
      *****
    }

  data <- df[1,1]
  return(data)
}

system.time({

  cl <- detectCores()
  cl <- makeCluster(cl)
  registerDoParallel(cl)

  mat <- t(parSapply(cl, list, myfuntion))

  stopCluster(cl)

}) 

我觉得这是由于parSapply占用了整个核心,所以foreach没有额外的核心来计算。有什么好主意来解决它吗?基本上我想实现两个进程在它们的并行版本中运行。

另一个问题是:假设我们只能选择一个进程进行并行计算,我应该选择哪一个?for 循环或应用系列?

非常感激 :)

标签: rfor-loopparallel-processingapplydoparallel

解决方案


我觉得这是由于 parSapply 占据了整个核心,所以 foreach 没有额外的核心来计算。有什么好主意来解决它吗?基本上我想实现两个进程在它们的并行版本中运行。

不,这不是一个好主意。您基本上是在这里尝试过度并行化(但这确实发生在您的代码中,如下所述)。

另一个问题是:假设我们只能选择一个进程进行并行计算,我应该选择哪一个?for 循环或应用系列?

没有一个正确的答案。我建议您分析您的*** process ***代码,以了解它从并行化中获得了多少收益。

所以,我发现你parSapply(cl, ...)在上面foreach() %dopar% { ... }使用相同的集群cl很有趣。我第一次看到以这种方式提出/提出的问题。您肯定不想这样做,但问题/尝试并不疯狂。foreach() %dopar% { ... }你的直觉是,当尝试使用它们时,所有的工人都会被占用,这部分是正确的。然而,真正发生的事情还在于,该foreach() %dopar% { ... }语句是在工作人员中评估的,而不是在定义集群的主 R 会话中cl。在 worker 上,没有注册 foreach 适配器,因此这些调用将默认为顺序处理 (== foreach::registerDoSEQ())。要实现嵌套并行化,您必须在每个工作程序中(例如在myfunction()函数内部)设置和注册一个集群。

作为未来框架的作者,我想建议你利用它。它将保护您免受上述错误的影响,并且也不会过度并行(如果您真的想这样做,您可以这样做)。以下是我将如何重写您的代码示例:

library(foreach) ## foreach() and %dopar%

myfunction <- function{data} {
   df  <- foreach(i = 1:200, .combine = "rbind") %:% 
    foreach(j = 1:200, .combine = "rbind") %dopar% {
      *****
      process
      *****
    }

  data <- df[1,1]
  return(data)
}


## Tell foreach to parallelize via the future framework
doFuture::registerDoFuture()

## Have the future framework parallelize using a cluster of
## local workers (similar to makeCluster(detectCores()))
library(future)
plan(multisession)

library(future.apply) ## future_sapply()

system.time({
  mat <- t(future_sapply(list, myfuntion))
})

现在,重要的是要了解外部future_sapply()并行化将在“多会话”集群上运行。当您进入内部foreach() %dopar% { ... }并行化时,foreach 所看到的只是一个顺序工作器,因此内部层将被并行处理。这就是我的意思,未来的框架会自动保护你免受过度并行化。

如果您想让内层在“多会话”集群上并行化,而外层是顺序的,您可以将其设置为:

plan(list(sequential, multisession))

如果你真的想做嵌套并行化,比如说,两个外层工作人员和四个内层工作人员,你可以使用:

plan(list(tweak(multisession, workers = 2), tweak(multisession, workers = 4))

这将同时运行 2*4 = 8 个并行 R 进程。

更有用的是当您有多台机器可用时,您可以将它们用于外层,然后在每台机器上使用多会话集群。就像是:

plan(list(tweak(cluster, workers = c("machine1", "machine2")), multisession))

您可以在以后的小插曲中阅读更多相关信息。


推荐阅读