我正在使用for循环计算置换检验统计量。我希望使用并行处理(特别是foreach包中的foreach)加快速度。我按照以下说明操作: https://beckmw.wordpress.com/2014/01/21/a-brief-foray-into-parallel-processing-with-r/
我的原始代码:
library(foreach)
library(doParallel)
set.seed(10)
x = rnorm(1000)
y = rnorm(1000)
n = length(x)
nexp = 10000
perm.stat1 = numeric(n)
ptm = proc.time()
for (i in 1:nexp){
y = sample(y)
perm.stat1[i] = cor(x,y,method = "pearson")
}
proc.time()-ptm
# 1.321 seconds
然而,当我使用foreach循环时,我得到的结果要慢得多:
cl<-makeCluster(8)
registerDoParallel(cl)
perm.stat2 = numeric(n)
ptm = proc.time()
perm.stat2 = foreach(icount(nexp), .combine=c) %dopar% {
y = sample(y)
cor(x,y,method = "pearson")
}
proc.time()-ptm
stopCluster(cl)
#3.884 seconds
为什么会这样?我做错了什么? 感谢
答案 0 :(得分:1)
您的性能会下降,因为您将一个小问题分解为 10,000个任务,每个任务需要执行八分之一毫秒。当循环体需要很长一段时间(我曾经说过至少10秒,但是我已经把它放到了处)时,将for
循环简单地转换为foreach
循环是没关系的。现在至少一秒钟),但当任务非常小时(在这种情况下,极小),这种简单的策略不起作用。当任务很小时,您将花费大部分时间来发送任务并从工作人员那里接收结果。换句话说,通信开销大于计算时间。坦率地说,我很惊讶你没有表现得更差。
对我来说,并行处理执行时间不到两秒的问题并不值得,但实际上可以通过 chunking 使用foreach
来加速。也就是说,您将问题拆分为较小的块,通常为每个工作者提供一个块。这是一个例子:
nw <- getDoParWorkers()
perm.stat1 <-
foreach(xnexp=idiv(nexp, chunks=nw), .combine=c) %dopar% {
p = numeric(xnexp)
for (i in 1:xnexp) {
y = sample(y)
p[i] = cor(x,y,method="pearson")
}
p
}
正如您所看到的,foreach
循环将问题拆分为块,并且该循环的主体包含原始顺序代码的修改版本,现在只占整个问题的一小部分。
在我的四核Mac笔记本电脑上,这在0.447秒内执行,而顺序版本为1.245秒。这对我来说似乎是一个非常可观的速度。
答案 1 :(得分:0)
foreach
循环中有更多的计算开销。这将返回一个列表,其中包含循环体的每次执行,然后通过.combine=c
参数将其合并到一个向量中。 for
循环不会返回任何内容,而是将值perm.stat1
指定为副作用,因此不需要任何额外开销。
请查看Why is foreach() %do% sometimes slower than for?,以便更详深地解释为什么foreach
在许多情况下比for
慢foreach
。 rnorm
自成一体的是当循环内的操作是计算密集的时候,通过比较使得返回列表中的每个值相关联的时间损失是微不足道的。例如,上面的Wordpress文章中使用的summary
和{{1}}的组合。