2012-05-02 24 views
10

Po raz pierwszy bawię się równolegle z R. Jako pierwszy przykład zabawek, próbowałemDlaczego foreach()% do% czasami wolniej niż dla?

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

wiem, że sqrt() wykonuje zbyt szybko parallellization do znaczenia, ale co ja nie spodziewałem się, że foreach() %do% byłby wolniejszy niż 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 

W większości przykładów, które widziałem, foreach() %dopar% jest porównywany z foreach() %do% zamiast for(). Od foreach() %do% był znacznie wolniejszy niż for() w moim przykładzie zabawki, jestem teraz nieco zdezorientowany. W jakiś sposób pomyślałem, że są to równoważne sposoby konstruowania pętli for. Jaka jest różnica? Czy są one kiedykolwiek ekwiwalentne? Czy foreach() %do% jest zawsze wolniejszy?

UPDATE: Po @Peter Grzywny odpowiedzieć zaktualizować myFunc następująco:

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

To sprawia for() nieco wolniej, ale nie za dużo:

> 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

Zobacz także to pytanie: http://stackoverflow.com/questions/5007458/problems-using-foreach- parallelisation i this: http: // stackoverflow.com/questions/5012804/mpi-parallelization-using-snow-is-slow – Charlie

+0

Dzięki @Charlie, odpowiedzi na te pytania były bardzo pomocne w tym, co próbuję zrobić po tym, jak skończyłem z moim przykładem zabawkowym! :) Wciąż nie jestem pewien, czy rozumiem, dlaczego 'foreach' potrzebuje o wiele więcej czasu przy użyciu opcji'% do% '. –

+0

Duża część tego polega na tym, że% do% musi pakować kawałki/przypisania, przesyłać je do procesorów, a następnie ponownie dołączać do wszystkiego na końcu, stosownie do potrzeb. Te kroki wymagają czasu organizacyjnego, którego nie można porównać z niezrównaną wersją. – Charlie

Odpowiedz

8

for potrwa sqrt razy B, prawdopodobnie za każdym razem odrzucają odpowiedź. foreach zwraca jednak listę zawierającą wynik każdego wykonania treści pętli. Spowodowałoby to znaczny dodatkowy narzut, niezależnie od tego, czy działa w trybie równoległym, czy sekwencyjnym (%dopar% lub %do%).

Oparłem swoją odpowiedź, uruchamiając następujący kod, który wydaje się być potwierdzony przez foreach vignette, który stwierdza, że ​​"foreach różni się od pętli for, że jego powrót jest listą wartości, podczas gdy pętla for nie ma wartości i wykorzystuje efekty uboczne, aby przekazać jej wynik. "

> 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 

AKTUALIZACJA: Z zaktualizowanego pytania wynika, że ​​powyższa odpowiedź nie jest wystarczająca do uwzględnienia różnicy w wydajności. Więc patrzyłem na source code dla foreach i widzę, że jest LOT! Nie próbowałem dokładnie zrozumieć, jak to działa, jednak do.R i foreach.R pokazują, że nawet po uruchomieniu %do%, duże części konfiguracji foreach nadal działają, co miałoby sens, jeśli opcja %do% jest w dużej mierze przewidziana, aby umożliwić użytkownikowi Przetestuj kod foreach bez konieczności skonfigurowania i załadowania równoległego zaplecza. Musi również obsługiwać bardziej zaawansowane funkcje zagnieżdżania i iteracji, które zapewnia foreach.

Istnieją odniesienia w kodzie do buforowania wyników, sprawdzania błędów, debugowania i tworzenia lokalnych zmiennych środowiskowych dla argumentów każdej iteracji (patrz na przykład funkcja doSEQ w do.R). Wyobrażam sobie, że to właśnie tworzy różnicę, którą zaobserwowałeś. Oczywiście, gdybyś wykonywał znacznie bardziej skomplikowany kod w swojej pętli (który faktycznie skorzystałby z architektury równoległej, takiej jak foreach), to narzut ten stałby się nieistotny w porównaniu z korzyściami, jakie zapewnia.

+0

Dobrze - to powinno wyjaśniać przynajmniej część różnicy! Nadal nie jestem pewien, czy to wszystko wyjaśnia; zobacz aktualizację mojego pytania! –

+0

@MansT, czy moja aktualizacja pomaga w wyjaśnieniu rzeczy? –

+0

Tak, dziękuję, to dużo wyjaśnia :) –

Powiązane problemy