2012-05-02 16 views
10

Io gioco per la prima volta con la parallellizzazione in R. Come primo esempio giocattolo, ho provatoPerché foreach()% do% a volte è più lento di?

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

so che sqrt() esegue troppo veloce per parallellization alla materia, ma quello che non mi aspettavo era che foreach() %do% sarebbe più lento di 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 

Nella maggior parte degli esempi che ho visto, lo foreach() %dopar% viene confrontato con lo foreach() %do% anziché con lo for(). Dal foreach() %do% era molto più lento di for() nell'esempio del mio giocattolo, ora sono un po 'confuso. In qualche modo, pensavo che questi fossero modi equivalenti per costruire for-loops. Qual è la differenza? Sono mai equivalenti? foreach() %do% è sempre più lento?

UPDATE: seguenti ammende @Peter rispondere, aggiorno myFunc come segue:

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

Questo rende for() un po 'più lento, ma non di molto:

> 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

Vedere anche questa domanda: http://stackoverflow.com/questions/5007458/problems-using-foreach-parallelization e questo: http: // stackoverflow.it/questions/5012804/mpi-parallelization-using-snow-is-slow – Charlie

+0

Grazie a @Charlie, le risposte a queste domande sono state molto utili a quello che sto cercando di fare dopo aver finito con il mio esempio di giocattolo! :) Non sono ancora sicuro di aver capito perché 'foreach' ha bisogno di molto più tempo quando si usa l'opzione'% do% '. –

+0

Gran parte di questo è che% do% deve suddividere i pezzi/assegnazioni, inviarli ai processori, quindi riunire tutto alla fine, a seconda dei casi. Questi passaggi richiedono tempi organizzativi che la versione non parallela non ha. – Charlie

risposta

8

for verrà eseguito sqrt volte B, presumibilmente scartando la risposta ogni volta. foreach, tuttavia, restituisce una lista contenente il risultato di ogni esecuzione del corpo del ciclo. Ciò comporterebbe un considerevole overhead aggiuntivo, indipendentemente dal fatto che sia in esecuzione in modalità parallela o sequenziale (%dopar% o %do%).

Ho basato la mia risposta eseguendo il seguente codice, che sembra essere confermato dal foreach vignette, che afferma "foreach differisce da un ciclo for in quanto il suo ritorno è un elenco di valori, mentre un ciclo for non ha valore e usa gli effetti collaterali per trasmettere il suo risultato. "

> 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 

UPDATE: Vedo dalla tua domanda aggiornato che la risposta di cui sopra non è quasi sufficiente a spiegare la differenza di prestazioni. Così ho guardato lo source code per foreach e posso vedere che c'è un LOT in corso! Non ho cercato di capire esattamente come funziona, tuttavia do.R e foreach.R mostrano che anche quando viene eseguito %do%, vengono ancora eseguite grandi parti della configurazione foreach, il che avrebbe senso se l'opzione %do% è in gran parte fornita per consentire di prova codice foreach senza che sia necessario configurare e caricare un backend parallelo. È inoltre necessario supportare le strutture di nidificazione e iterazione più avanzate fornite da foreach.

Ci sono riferimenti nel codice alla cache dei risultati, al controllo degli errori, al debug e alla creazione di variabili di ambiente locali per gli argomenti di ogni iterazione (vedere la funzione doSEQ in do.R per esempio). Immagino che questo sia ciò che crea la differenza che hai osservato. Naturalmente, se si eseguisse codice molto più complicato all'interno del ciclo (che trarrebbe effettivamente vantaggio da un framework di parallelizzazione come foreach), questo sovraccarico sarebbe irrilevante rispetto ai vantaggi che offre.

+0

Giusto: questo dovrebbe spiegare almeno parte della differenza! Non sono ancora sicuro se lo spiega tutto; guarda l'aggiornamento alla mia domanda! –

+0

@MansT, il mio aggiornamento aiuta a spiegare le cose? –

+0

Sì, grazie, questo spiega molto :) –

Problemi correlati