问题 为什么R for循环比使用foreach慢10倍?


这真让我大吃一惊。我的电脑基本循环需要8秒钟:

system.time({
x <- 0
for (p in 1:2) {
    for (i in 1:500) {
        for (j in 1:5000) {
            x <- x + i * j
        }
    }
}
})
x

而如果我使用 foreach 在非并行模式下,它只需要0.7秒!

system.time({
x <- 0
foreach(p = 1:2, .combine = rbind) %do% 
    for (i in 1:500) {
        for (j in 1:5000) {
            x <- x + i * j
        }
    }
})
x

结果是一样的,但是 foreach 以某种方式能够比基本R更快地到达它!基本R的低效率在哪里?

这怎么可能?

事实上,与此相比,我得到了完全相反的结果: 为什么foreach()%do%有时慢于?


7657
2017-07-09 10:49


起源

这是编写程序包如何改进/增强基本方法的完美示例。 - Rich Scriven
@Richard很好,如果你理解为什么和发生了什么,请发表回答。 - TMS
代码被编译,最终由 make.codeBuf - James
@James,是的,听起来像这样! - TMS
因此,如果您采用三重循环,将其转换为函数,并使用 compile::cmpfun,结果函数会如此快 foreach ? - Carl Witthoft


答案:


foreach 当顺序使用时最终使用 compiler 使用非导出函数生成编译的字节代码 make.codeBuf 和 cmp。您可以使用 cmpfun 将innerloop编译成字节码来模拟这个并实现类似的加速。

f.original <- function() {
x <- 0
for (p in 1:2) {
    for (i in 1:500) {
        for (j in 1:5000) {
            x <- x + i * j
        }
    }
}
x
}

f.foreach <- function() {
x <- 0
foreach(p = 1:2, .combine = rbind) %do% 
    for (i in 1:500) {
        for (j in 1:5000) {
            x <- x + i * j
        }
    }
x
}

f.cmpfun <- function(x) {
f <- cmpfun(function(x) {
    for (i in 1:500) {
        for (j in 1:5000) {
            x <- x + i * j
            }
        }
        x
    })
    f(f(0))
}

结果

library(microbenchmark)
microbenchmark(f.original(),f.foreach(),f.cmpfun(), times=5)
Unit: milliseconds
         expr       min        lq    median        uq       max neval
 f.original() 4033.6114 4051.5422 4061.7211 4072.6700 4079.0338     5
  f.foreach()  426.0977  429.6853  434.0246  437.0178  447.9809     5
   f.cmpfun()  418.2016  427.9036  441.7873  444.1142  444.4260     5
all.equal(f.original(),f.foreach(),f.cmpfun())
[1] TRUE

9
2017-07-09 12:43



很好,谢谢! - TMS
干得好,谢谢你们 cmpfun 时间测试。 - Carl Witthoft