我正在尝试比较并行化选项。具体来说,我将标准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
实施:
library(snow)
cl <- makeCluster(2)
ParCltSim <- function(cluster, nSims=1000, size=100, mu=0, sigma=1){
parSapply(cluster, 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))
}
}
我得到以下结果:
> system.time(CltSim(nSims=10000, size=100))
user system elapsed
0.476 0.008 0.484
> system.time(ParCltSim(cluster=cl, nSims=10000, size=100))
user system elapsed
0.028 0.004 0.375
> system.time(FECltSim(nSims=10000, size=100))
user system elapsed
8.865 0.408 11.309
SNOW
实现相对于非平行运行削减了大约23%的计算时间(随着模拟数量的增加,时间节省变得更大,正如我们所期望的那样)。 foreach
尝试实际增加运行时间20倍。此外,如果我将%dopar%
更改为%do%
并检查循环的非平行版本,则需要7秒钟。
此外,我们可以考虑multicore
包。为multicore
编写的模拟是
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))
}))
}
我们获得比SNOW
更快的速度提升:
> system.time(MCCltSim(nSims=10000, size=100))
user system elapsed
0.924 0.032 0.307
开始新的R会话,我们可以使用foreach
代替doMC
尝试doSNOW
实施,并致电
library(doMC)
registerDoMC()
然后如上所述运行FECltSim()
,仍然找到
> system.time(FECltSim(nSims=10000, size=100))
user system elapsed
6.800 0.024 6.887
这比非并行化运行时“仅”增加了14倍。
结论:我的foreach
代码无法在doSNOW
或doMC
下有效运行。知道为什么吗?
谢谢, 查理
答案 0 :(得分:5)
要遵循Joris所说的内容,foreach()
最适合的工作数量并不会超过您将使用的处理器数量。或者更一般地说,当每个工作自己花费大量时间(比如几秒或几分钟)。创建线程有很多开销,所以你真的不想将它用于很多小工作。如果你正在做1000万个sim而不是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()
用于此类应用而不是使用。实际上,foreach
包具有类似的便利功能times()
,可以在这种情况下应用。当然,如果您的代码每次都没有使用相同的参数进行简单的模拟,则需要sapply()
和foreach()
。
答案 1 :(得分:4)
首先,您可以更简洁地编写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))
}
}
这为您提供了一个向量,无需在循环中明确地使用它。也不需要使用cbind,因为你的结果每次只是一个数字。所以.combine=c
会做
与foreach相关的是,它在内核之间进行通信会产生相当多的开销,并且可以将不同内核的结果放在一起。快速查看配置文件可以清楚地看到这一点:
$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
...
超过40%的时间忙于选择东西。它还为整个操作使用了许多其他功能。实际上,foreach
仅在通过非常耗时的函数进行相对较少的轮次时才是可取的。
另外两个解决方案是基于不同的技术构建的,并且在R中做得少得多。在一个副节点上,snow
实际上最初是针对集群而不是单个工作站开发的,例如multicore
是