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

Mån*_*nsT 10 parallel-processing r

我第一次在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)
}
Run Code Online (Sandbox Code Playgroud)

我知道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 
Run Code Online (Sandbox Code Playgroud)

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

更新:关于@Peter罚款回答,我更新myFunc如下:

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

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 
Run Code Online (Sandbox Code Playgroud)

Pet*_*ine 8

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

我通过运行以下代码来建立我的答案,该代码似乎由foreach插图确认,其中指出"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
Run Code Online (Sandbox Code Playgroud)

更新:我从您更新的问题中看到,上述答案几乎不足以说明性能差异.所以我看了看源代码foreach,可以看到,有很多事情!我还没有尝试准确理解它是如何工作的,但是do.R并且foreach.R表明即使在%do%运行时,foreach仍然会运行大部分配置,如果可能提供的%do%选项很大程度上允许您测试foreach代码而不必有一个并行后端配置和加载.它还需要支持更高级的嵌套和迭代工具foreach.

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