parallel r parallel-processing

parallel - ¿Por qué foreach()% do% a veces es más lento que para?



foreach r (1)

Estoy jugando con la paralelización en R por primera vez. Como primer ejemplo de juguete, lo intenté

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

Sé que sqrt() ejecuta demasiado rápido para que la paralelización importe, pero lo que no esperaba era que foreach() %do% fuera más lento que 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

En la mayoría de los ejemplos que he visto, foreach() %dopar% se compara con foreach() %do% lugar de for() . Como foreach() %do% fue mucho más lento que for() en mi ejemplo de juguete, ahora estoy un poco confundido. De alguna manera, pensé que estas eran formas equivalentes de construir for-loops. ¿Cuál es la diferencia? ¿Alguna vez son equivalentes? ¿ foreach() %do% siempre es más lento?

ACTUALIZACIÓN: Después de la respuesta de @Peter Fines, actualizo myFunc siguiente manera:

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

Esto hace for() un poco más lento, pero no mucho:

> system.time(myFunc()) user system elapsed 0.036 0.000 0.035 > system.time(myFunc2()) user system elapsed 6.380 0.000 6.385


for ejecutará sqrt B veces, presumiblemente descartando la respuesta cada vez. foreach , sin embargo, devuelve una lista que contiene el resultado de cada ejecución del cuerpo del bucle. Esto contribuiría a una considerable sobrecarga adicional, independientemente de si se está ejecutando en modo paralelo o secuencial ( %dopar% o %do% ).

Basé mi respuesta ejecutando el siguiente código, que parece ser confirmado por la viñeta foreach , que dice "foreach difiere de un ciclo for en que su resultado es una lista de valores, mientras que un ciclo for no tiene valor y usa efectos secundarios para transmitir su resultado ".

> 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

ACTUALIZACIÓN: veo en su pregunta actualizada que la respuesta anterior no es suficiente para explicar la diferencia en el rendimiento. ¡Así que miré el código fuente para foreach y puedo ver que MUCHO está pasando! No he intentado comprender exactamente cómo funciona, sin embargo, do.R y foreach.R muestran que incluso cuando se ejecuta %do% se ejecutan grandes partes de la configuración foreach , lo que tendría sentido si tal vez el %do% La opción se proporciona principalmente para permitirle probar el código foreach sin tener que tener un servidor paralelo configurado y cargado. También necesita admitir las instalaciones más avanzadas de anidación e iteración que foreach proporciona.

Hay referencias en el código para el almacenamiento en caché de resultados, la comprobación de errores, la depuración y la creación de variables de entorno locales para los argumentos de cada iteración (consulte la función doSEQ en do.R por ejemplo). Me imagino que esto es lo que crea la diferencia que has observado. Por supuesto, si ejecutara un código mucho más complicado dentro de su ciclo (que en realidad se beneficiaría de un marco de paralelización como foreach ), esta sobrecarga sería irrelevante en comparación con los beneficios que proporciona.