为什么foreach()%do%有时慢于?

时间:2012-05-02 10:25:37

标签: r parallel-processing

我第一次在R中进行并行化。作为第一个玩具示例,我试过

library(doMC)
registerDoMC()

B<-10000

myFunc<-function()
{
    for(i in 1:B) sqrt(i)
}

myFunc2<-function()
{
    foreach(i = 1:B)  %do% sqrt(i)
}

myParFunc<-function()
{
    foreach(i = 1:B) %dopar% sqrt(i)
}

我知道sqrt()执行速度太快,无法实现并行化,但我没想到的是foreach() %do%会慢于for()

> system.time(myFunc())
   user  system elapsed 
  0.004   0.000   0.005 
> system.time(myFunc2())
   user  system elapsed 
  6.756   0.000   6.759 
> system.time(myParFunc())
   user  system elapsed 
  6.140   0.524   6.096 

在我看过的大多数示例中,foreach() %dopar%foreach() %do%而非for()进行比较。由于foreach() %do%在我的玩具示例中比for()慢得多,我现在有点困惑。不知何故,我认为这些是构造for循环的等效方法。有什么不同?它们是否相同? foreach() %do%总是慢一点吗?

更新:关注@Peter Fines回答,我更新myFunc如下:

 a<-rep(NA,B)
 myFunc<-function()
 {
     for(i in 1:B) a[i]<-sqrt(i)
 }

这会使for()慢一点,但不会太多:

> system.time(myFunc())
   user  system elapsed 
  0.036   0.000   0.035 
> system.time(myFunc2())
   user  system elapsed 
  6.380   0.000   6.385 

1 个答案:

答案 0 :(得分:8)

for将运行sqrt次B次,可能每次丢弃答案。但是,foreach返回一个列表,其中包含循环体每次执行的结果。无论是以并行模式还是顺序模式(%dopar%%do%)运行,这都会带来相当大的额外开销。

我通过运行以下代码来建立我的答案,该代码似乎由foreach vignette确认,其中指出“foreach与for循环的不同之处在于它的返回值是值列表,而for循环有没有价值,并使用副作用来传达其结果。“

> print(for(i in 1:10) sqrt(i))
NULL

> print(foreach(i = 1:10) %do% sqrt(i))
[[1]]
[1] 1

[[2]]
[1] 1.414214

[[3]]
... etc

更新:我从您更新的问题中看到,上述答案几乎不足以说明性能差异。所以我查看foreach的{​​{3}},可以看到有很多事情发生了!我没有试图准确理解它是如何工作的,但do.Rforeach.R表明即使运行%do%foreach配置的大部分仍在运行,如果可能提供%do%选项以允许您测试foreach代码而无需配置和加载并行后端,那么这将是有意义的。它还需要支持foreach提供的更高级的嵌套和迭代功能。

代码中有一些引用用于结果缓存,错误检查,调试以及为每次迭代的参数创建局部环境变量(例如,参见doSEQ中的函数do.R)。我想这就是你所观察到的差异。当然,如果你在循环中运行更复杂的代码(实际上会从像foreach这样的并行化框架中受益),与它提供的好处相比,这种开销将变得无关紧要。