Pytanie Dlaczego R dla pętli jest 10 razy wolniejsze niż podczas korzystania z foreach?


To naprawdę dmucha w moim umyśle. Podstawowa pętla trwa około 8 sekund na moim komputerze:

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

Natomiast jeśli używam foreach w trybie innym niż równoległy zajmuje tylko 0,7 s !!!

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

Wynik jest taki sam, ale foreach był w stanie dotrzeć do niego znacznie szybciej niż podstawowy R! Gdzie jest nieskuteczność podstawowego R?

Jak to jest możliwe?

W rzeczywistości otrzymałem kompletny wynik przeciwny w porównaniu do tego: Dlaczego foreach ()% do% czasami jest wolniejsze niż dla?


11
2017-07-09 10:49


pochodzenie


Jest to doskonały przykład na to, jak napisanie paczki może poprawić lub ulepszyć podstawowe metody. - Rich Scriven
@Richard świetnie, proszę, jeśli rozumiesz, dlaczego i co się dzieje, opublikuj odpowiedź. - TMS
Kod zostanie skompilowany, ostatecznie przez make.codeBuf - James
@ James, tak, to brzmi jak to! - TMS
Jeśli więc weźmiesz triple-for-loop, przekształć go w funkcję i użyj compile::cmpfun, wynikowa funkcja będzie tak szybka jak foreach ? - Carl Witthoft


Odpowiedzi:


foreach kiedy jest używany sekwencyjnie w końcu używa compiler do tworzenia skompilowanego kodu bajtowego przy użyciu nieeksportowanych funkcji make.codeBuf i cmp. Możesz użyć cmpfun aby skompilować pętlę wewnętrzną do kodu bajtowego, aby zasymulować to i osiągnąć podobne przyspieszenie.

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))
}

Wyniki

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



Perfekcyjnie, dzięki! - TMS
Świetna robota i dzięki za wykonanie cmpfun test czasowy. - Carl Witthoft