foreach并行化问题

foreach并行化问题,r,foreach,parallel-processing,R,Foreach,Parallel Processing,我试图比较并行化选项。具体来说,我将标准的SNOW和mulitcore实现与使用doSNOW或doMC和foreach的实现进行比较。作为一个样本问题,我通过多次计算标准正态分布样本的平均值来说明中心极限定理。以下是标准代码: CltSim <- function(nSims=1000, size=100, mu=0, sigma=1){ sapply(1:nSims, function(x){ mean(rnorm(n=size, mean=mu, sd=sigma))

我试图比较并行化选项。具体来说,我将标准的
SNOW
mulitcore
实现与使用
doSNOW
doMC
foreach
的实现进行比较。作为一个样本问题,我通过多次计算标准正态分布样本的平均值来说明中心极限定理。以下是标准代码:

CltSim <- function(nSims=1000, size=100, mu=0, sigma=1){
  sapply(1:nSims, function(x){
    mean(rnorm(n=size, mean=mu, sd=sigma))
  })
}
接下来,使用
doSNOW
方法:

library(foreach)
library(doSNOW)
registerDoSNOW(cl)

FECltSim <- function(nSims=1000, size=100, mu=0, sigma=1) {
  x <- numeric(nSims)
  foreach(i=1:nSims, .combine=cbind) %dopar% {
    x[i] <- mean(rnorm(n=size, mean=mu, sd=sigma))
  }
}
相对于非并行运行,
SNOW
实现节省了约23%的计算时间(正如我们所预期的,随着模拟次数的增加,节省的时间会越来越多)。
foreach
尝试实际上将运行时间增加了20倍。此外,如果我将
%dopar%
更改为
%do%
,并检查循环的未配置版本,则需要7秒以上的时间

另外,我们可以考虑<代码>多核< /代码>包。为

多核
编写的模拟是

library(multicore)
MCCltSim <- function(nSims=1000, size=100, mu=0, sigma=1){
  unlist(mclapply(1:nSims, function(x){
    mean(rnorm(n=size, mean=mu, sd=sigma))
  }))
}
启动新的R会话时,我们可以尝试使用
doMC
而不是
doSNOW
实现
foreach
,调用

library(doMC)
registerDoMC()
然后像上面一样运行
FECltSim()
,仍然可以找到

> system.time(FECltSim(nSims=10000, size=100))
   user  system elapsed 
  6.800   0.024   6.887 
与非并行运行时相比,这“仅”增加了14倍

结论:我的
foreach
代码在
doSNOW
doMC
下都不能有效运行。知道为什么吗

谢谢,
Charlie

首先,您可以编写更简洁的foreach代码:

FECltSim <- function(nSims=1000, size=100, mu=0, sigma=1) {
  foreach(i=1:nSims, .combine=c) %dopar% {
    mean(rnorm(n=size, mean=mu, sd=sigma))
  }
}
超过40%的时间它都在忙着挑选东西。它还为整个操作使用了许多其他功能。实际上,
foreach
只有在通过非常耗时的函数进行的回合数相对较少的情况下才是可取的


另外两个解决方案基于不同的技术,在R中的作用要小得多。在一个侧节点上,
snow
实际上最初开发用于集群而不是单个工作站,如
multi-core
is。

接下来是Joris所说的,
foreach()
当作业数量不超过您将使用的处理器数量时,这是最好的选择。或者更一般地说,当每项作业本身花费大量时间(比如秒或分钟)时。在创建线程时会有很多开销,所以你真的不想在很多小作业中使用它。如果您正在进行1000万模拟人生而不是1万模拟人生,并且您的代码结构如下:

nSims = 1e7
nBatch = 1e6
foreach(i=1:(nSims/nBatch), .combine=c) %dopar% {
  replicate(nBatch, mean(rnorm(n=size, mean=mu, sd=sigma))
}
我打赌你会发现foreach做得很好


还请注意,对于此类应用程序,使用了
replicate()
,而不是sapply。实际上,
foreach
包有一个类似的便利函数,
times()
,可以应用于这种情况。当然,如果您的代码不是每次都使用相同的参数进行简单的模拟,那么您需要
sapply()
foreach()

再次感谢Jons。实际上,我以前没有使用过Rprof,您能解释一下如何解释这个输出,或者给我指出一个可以解释的资源吗?我查看了R的本机帮助文件以获取
summaryRprof
,但没有那么大帮助。@Charlie:self.time是函数本身花费的时间。self.pct是函数本身花费的总时间的百分比。total.time是在该函数或其调用的任何其他函数中花费的时间。例:
f1谢谢。
$
$我检查了帮助文件(
summaryRprof
Rprof
),但他们没有讨论解释。@Charlie:我指的是.Call、“$”等帮助文件。谢谢你建议将流程分成批处理;我敢打赌,这会节省不少沟通时间。我以前看过
复制
,但没有看过
> system.time(FECltSim(nSims=10000, size=100))
   user  system elapsed 
  6.800   0.024   6.887 
FECltSim <- function(nSims=1000, size=100, mu=0, sigma=1) {
  foreach(i=1:nSims, .combine=c) %dopar% {
    mean(rnorm(n=size, mean=mu, sd=sigma))
  }
}
$by.self
                         self.time self.pct total.time total.pct
$                             5.46    41.30       5.46     41.30
$<-                           0.76     5.75       0.76      5.75
.Call                         0.76     5.75       0.76      5.75
...
nSims = 1e7
nBatch = 1e6
foreach(i=1:(nSims/nBatch), .combine=c) %dopar% {
  replicate(nBatch, mean(rnorm(n=size, mean=mu, sd=sigma))
}