Probleme mit foreach-Parallelisierung
Ich versuche zu vergleichen Parallelisierung Optionen. Speziell, ich Vergleiche den standard SNOW
und mulitcore
Implementierungen von denen doSNOW
oder doMC
und foreach
. Als ein Beispiel problem, ich bin zu illustrieren, central limit theorem durch die Berechnung der Mittel der gezogenen Proben aus einer standard-normal-Verteilung, viele Male. Hier ist die standard-code:
CltSim <- function(nSims=1000, size=100, mu=0, sigma=1){
sapply(1:nSims, function(x){
mean(rnorm(n=size, mean=mu, sd=sigma))
})
}
Hier ist die SNOW
Umsetzung:
library(snow)
cl <- makeCluster(2)
ParCltSim <- function(cluster, nSims=1000, size=100, mu=0, sigma=1){
parSapply(cluster, 1:nSims, function(x){
mean(rnorm(n=size, mean=mu, sd=sigma))
})
}
Nächsten, die doSNOW
Methode:
library(foreach)
library(doSNOW)
registerDoSNOW(cl)
FECltSim <- function(nSims=1000, size=100, mu=0, sigma=1) {
x <- numeric(nSims)
foreach(i=1:nSims, .combine=cbind) %dopar% {
x[i] <- mean(rnorm(n=size, mean=mu, sd=sigma))
}
}
Bekomme ich folgende Ergebnisse:
> system.time(CltSim(nSims=10000, size=100))
user system elapsed
0.476 0.008 0.484
> system.time(ParCltSim(cluster=cl, nSims=10000, size=100))
user system elapsed
0.028 0.004 0.375
> system.time(FECltSim(nSims=10000, size=100))
user system elapsed
8.865 0.408 11.309
Den SNOW
Implementierung Rasiert ab etwa 23% der Rechenzeit im Vergleich zu einem unparallelized laufen (time Einsparungen größer als die Anzahl der Simulationen zu erhöhen, wie wir es erwarten würden). Die foreach
Versuch eigentlich erhöht Laufzeit um einen Faktor von 20. Außerdem, wenn ich das ändern %dopar%
zu %do%
und überprüfen Sie die unparallelized version der Schleife, es dauert über 7 Sekunden.
Darüber hinaus berücksichtigen wir die multicore
Paket. Die simulation geschrieben multicore
ist
library(multicore)
MCCltSim <- function(nSims=1000, size=100, mu=0, sigma=1){
unlist(mclapply(1:nSims, function(x){
mean(rnorm(n=size, mean=mu, sd=sigma))
}))
}
Bekommen wir eine noch bessere Verbesserung der Geschwindigkeit als SNOW
:
> system.time(MCCltSim(nSims=10000, size=100))
user system elapsed
0.924 0.032 0.307
Starten eines neuen R-Sitzung, können wir versuchen, die foreach
Umsetzung mit doMC
statt doSNOW
Aufruf
library(doMC)
registerDoMC()
dann läuft FECltSim()
wie oben, noch zu finden
> system.time(FECltSim(nSims=10000, size=100))
user system elapsed
6.800 0.024 6.887
Dies ist "nur" eine 14-fache Erhöhung gegenüber den nicht-parallelisierte Laufzeit.
Fazit: Meine foreach
code ist nicht effizient läuft entweder unter doSNOW
oder doMC
. Irgendeine Idee warum?
Dank,
Charlie
Du musst angemeldet sein, um einen Kommentar abzugeben.
Mit zu beginnen, könnten Sie schreiben Ihre foreach-code ein wenig übersichtlicher :
Dies gibt Ihnen einen Vektor, nicht erforderlich, explizit zu machen, die es innerhalb der Schleife. Auch keine Notwendigkeit zu verwenden cbind, wie dein Ergebnis ist jedes mal nur eine einzige Zahl. So
.combine=c
tunDie Sache mit foreach ist, dass es erzeugt sehr viel overhead für die Kommunikation zwischen den Kernen und um die Ergebnisse der verschiedenen Kerne zueinander passen. Ein kurzer Blick auf das Profil zeigt dies ziemlich deutlich :
Mehr als 40% der Zeit damit beschäftigt ist die Auswahl Dinge. Es verwendet auch eine Menge anderer Funktionen für den gesamten Betrieb. Eigentlich
foreach
ist nur ratsam, wenn Sie mit relativ wenigen Runden durch sehr zeitaufwendige Funktionen.Den beiden anderen Lösungen basieren auf einer anderen Technologie, und nicht viel weniger in R. Auf ein sidenode,
snow
ist eigentlich ursprünglich entwickelt, um die Arbeit auf Clustern mehr als auf einzelnen Arbeitsplätzen, wiemulticore
ist.summaryRprof
und es war nicht so hilfreich.f1 <- function(x){f2(x)}
dann selbst.time ist die Zeit, inf1()
allein und total.time ist die Zeit, inf1()
undf2()
-falls Aufruf von f1() !. insgesamt.pct ist wieder der Prozentsatz der gesamten Zeit. Es ist ein bisschen verwirrend am Anfang, aber sehr mächtig für die Optimierung.$
,$<-
,.Call
, und ich vermute, anderen? Gibt es ein Dokument, das erklärt, was jeder von Ihnen stellt?summaryRprof
undRprof
), aber Sie hat sich nicht über die interpretation.Folgen auf etwas, Joris sagte,
foreach()
ist am besten, wenn die Zahl der Arbeitsplätze nicht enorm höher als die Zahl der Prozessoren, die Sie verwenden werden. Oder allgemein, wenn jeder job dauert eine erhebliche Menge an Zeit auf Ihrer eigenen (Sekunden oder Minuten sagen). Es ist eine Menge Aufwand bei der Erstellung der threads, so dass Sie wirklich nicht wollen, es zu benutzen für viele kleine jobs. Wenn Sie waren gerade dabei, 10 Millionen sims eher als 10 tausend, und Sie strukturiert Ihren code wie folgt:Ich Wette, Sie würde finden, dass die foreach war ziemlich gut.
Beachten Sie auch die Verwendung von
replicate()
für diese Art der Anwendung eher als sapply. Tatsächlich, dieforeach
Paket hat einen ähnlichen Komfort Funktiontimes()
, die angewandt werden könnten, in diesem Fall. Natürlich, wenn Ihr code ist dabei nicht eine einfache Simulationen mit identischen Parametern jedes mal, müssen Siesapply()
undforeach()
.replicate
vor, aber nichttimes
.