So optimieren Sie mein R-Skript für die Verwendung von "Multicore"

15

Ich benutze GNU R auf einem Ubuntu-Lucid-PC mit 4 CPUs. Um alle 4 CPUs nutzen zu können, habe ich das Paket "r-cran-multicore" installiert. Da das Handbuch des Pakets keine praktischen Beispiele enthält, die ich verstehe, benötige ich Ratschläge zur Optimierung meines Skripts, um alle 4 CPUs nutzen zu können.

Mein Datensatz ist ein data.frame (P1 genannt) mit 50.000 Zeilen und 1600 Spalten. Ich möchte für jede Zeile das Maximum, die Summe und den Mittelwert berechnen. Mein Skript sieht folgendermaßen aus:

p1max <- 0
p1mean <- 0
p1sum <-0
plength <- length(P1[,1])
for(i in 1:plength){
   p1max <- c(p1max, max(P1[i,]))
   p1mean <- c(p1mean, mean(P1[i,]))
   p1sum <- c(p1sum, sum(P1[i,]))
}

Kann mir jemand sagen, wie ich das Skript ändern und ausführen soll, um alle 4 CPUs zu nutzen?

Produnis
quelle
Im obigen Programm ist ein Fehler aufgetreten: Die Zeile sollte "für (i in 1: plength)" lauten
Simon Byrne
Du bist ehrlich, danke!
Produnis
1
Gehört das nicht zu StackOverflow?
R_Coholic
1
Dies gehört zu StackOverflow. Hier gibt es überhaupt keine Statistikfrage. Nur eine allgemeine Programmierfrage.
JD Long

Antworten:

11

Verwenden Sie foreach und doMC . Die ausführliche Erklärung finden Sie hier . Ihr Skript wird die Zeile sehr wenig verändern

for(i in 1:plength){

sollte geändert werden in

foreach(i=1:plength) %dopar% { 

Die Voraussetzungen für ein Multitasking-Skript, das diese Pakete verwendet, sind:

library(foreach)
library(doMC)
registerDoMC()

Hinweis zur Vorsicht. Laut Dokumentation können Sie dies nicht in der GUI verwenden.

Benötigen Sie für Ihr Problem wirklich Multitasking? Ihr data.frame benötigt ungefähr 1,2 GB RAM, sodass er in Ihren Arbeitsspeicher passt. So können Sie einfach anwenden:

p1smry <- apply(P1,1,summary)

Das Ergebnis ist eine Matrix mit Zusammenfassungen jeder Zeile.

Sie können auch die Funktion mclapply verwenden, die im Paket multicore enthalten ist. Dann könnte Ihr Skript so aussehen:

loopfun <- function(i) {
     summary(P1[i,])
}

res <- mclapply(1:nrow(P1),loopfun)

Dies gibt die Liste zurück, wobei das i-te Element die Zusammenfassung der i-ten Zeile ist. Sie können es mit sapply in eine Matrix konvertieren

mres <- sapply(res,function(x)x)
mpiktas
quelle
vielen Dank. Sie haben Recht, dass mit "Übernehmen" das Skript optimiert werden könnte. Ich habe nur mein Skript als Minimalbeispiel verwendet, um die Nachricht zu übermitteln ... Vielen Dank, Ihre Antwort ist genau das, wonach ich gesucht habe !!
Produnis
15

Sie haben bereits eine Antwort darauf, wie Sie mehr als einen Kern verwenden können, aber das eigentliche Problem liegt in der Art und Weise, wie Sie Ihre Schleifen geschrieben haben. Erweitern Sie niemals Ihren Ergebnisvektor / Objekt bei jeder Iteration einer Schleife . Wenn Sie dies tun, zwingen Sie R, Ihren Ergebnisvektor / Ihr Ergebnisobjekt zu kopieren und zu erweitern, was alles Zeit kostet. Weisen Sie stattdessen genügend Speicherplatz vorab zu, bevor Sie die Schleife starten, und füllen Sie diese bei Bedarf aus. Hier ist ein Beispiel:

set.seed(1)
p1 <- matrix(rnorm(10000), ncol=100)
system.time({
p1max <- p1mean <- p1sum <- numeric(length = 100)
for(i in seq_along(p1max)){
   p1max[i] <- max(p1[i,])
   p1mean[i] <- mean(p1[i,])
   p1sum[i ]<- sum(p1[i,])
}
})

   user  system elapsed 
  0.005   0.000   0.005

Oder Sie können diese Dinge tun über apply():

system.time({
p1max2 <- apply(p1, 1, max)
p1mean2 <- apply(p1, 1, mean)
p1sum2 <- apply(p1, 1, sum)
})
   user  system elapsed 
  0.007   0.000   0.006 

Beachten Sie jedoch, dass dies nicht schneller ist, als die Schleife ordnungsgemäß und manchmal langsamer auszuführen.

Achten Sie jedoch immer auf vektorisierten Code. Sie können Zeilensummen und Mittelwerte mitrowSums() und rowMeans()erstellen, die schneller als die Schleife oder die applyVersionen sind:

system.time({
p1max3 <- apply(p1, 1, max)
p1mean3 <- rowMeans(p1)
p1sum3 <- rowSums(p1)
})

   user  system elapsed 
  0.001   0.000   0.002 

Wenn ich ein Wettmann wäre, hätte ich Geld für den dritten Ansatz, den ich beim Schlagen erwähne, foreach()oder für die anderen Optionen mit mehreren Kernen in einem Geschwindigkeitstest für Ihre Matrix, weil sie die Dinge erheblich beschleunigen müssten, um den Mehraufwand beim Einrichten der zu rechtfertigen separate Prozesse, die auf die verschiedenen CPU-Kerne verteilt werden.

Update: Nach dem Kommentar von @shabbychef ist es schneller, die Summen einmal zu machen und bei der Berechnung des Mittelwerts wiederzuverwenden?

system.time({
    p1max4 <- apply(p1, 1, max)
    p1sum4 <- rowSums(p1)
    p1mean4 <- p1sum4 / ncol(p1)
    })

   user  system elapsed 
  0.002   0.000   0.002

Nicht in diesem Testlauf, aber das ist alles andere als erschöpfend ...

Setzen Sie Monica - G. Simpson wieder ein
quelle
FWIW, Matlab hat die gleichen Probleme in Bezug auf die Vorbelegung und das Erweitern von Vektoren und ist ein klassischer Code-Blooper. Zusätzlich zu Ihrem Einsatz ist es wahrscheinlich schneller, die Ergebnisse von rowSumszu verwenden, um das Zeilenmittel zu berechnen (es sei denn, mir fehlt etwas in Bezug auf z. B. Na oder NaN). Der Code in Ihrem dritten Ansatz summiert jede Spalte zweimal .
Shabbychef
@shabbychef Sie werden überrascht sein (siehe meine bearbeitete Antwort). Ja , die Summen fiktiv zweimal berechnet werden, aber rowSumsund rowMeanssind sehr kompilierten Code optimiert und was gewinnen wir in nur einmal die Summen der Berechnung, verlieren wir wieder in die mittlere Berechnung in interpretierten Code zu tun.
Setzen Sie Monica - G. Simpson am
@Gavin Simpson: nicht so schnell: versuche es stattdessen system.time({ for (iii in c(1:1000)) { p1max3 <- apply(p1, 1, max) p1mean3 <- rowMeans(p1) p1sum3 <- rowSums(p1) } })und auf ähnliche Weise system.time({ for (iii in c(1:1000)) { p1max4 <- apply(p1, 1, max) p1sum4 <- rowSums(p1) p1mean4 <- p1sum4 / ncol(p1) } }); Die Version, die die Summe nicht neu berechnet, benötigt auf meinem Computer 1,368 Sekunden. der, der das tut, braucht 1.396. wieder
alles andere als
@shabbychef müssen wir verschiedene Ideen haben , was ist oder nicht zwingend ;-) In der Tat, Ihre strengere Simulationen mein Hauptpunkt verstärken, dass rowMeansund rowSumsin effiziente, optimierte kompilierten Code implementiert werden , sie gehen nur schwer zu schlagen sein.
Setzen Sie Monica - G. Simpson am
@Gavin Simpson. Tatsächlich besteht das Problem bei meinem Beispiel darin, dass die meiste Zeit im Apply-Teil benötigt wird, um das Maximum zu berechnen. Ich stimme Ihnen zu, dass eine c-basierte vektorisierte Funktion wie rowMeandiese mit einem Allzweck-R-Tool schwer zu übertreffen sein wird *apply. Sie scheinen jedoch anzunehmen, dass es schneller ist, 10000 Zahlen zweimal über rowMeanund rowSumnicht nur einmal zu summieren und den in R eingebauten Divisionsoperator zu verwenden. Ich weiß, dass R einige Effizienzprobleme hat ( z. B. die jüngste Entdeckung des Problems der geschweiften Klammern gegenüber der Klammer), aber das scheint verrückt zu sein.
Shabbychef
1

Schauen Sie sich die Schnee- und Schneefallpakete an. Viele Beispiele mit denen ...

Wenn Sie diesen bestimmten Code beschleunigen möchten, anstatt R und Parallelität zu lernen, sollten Sie dies tun

P1 = matrix(rnorm(1000), ncol=10, nrow=10
apply(P1, 1, max)
apply(P1, 1, mean)
apply(P1, 1, sum)
Dr. G
quelle
Bitte helfen Sie mir, mein Skript zu ändern ...
Produnis
2
Diese verstecken nur die Schleife vor dir. Das eigentliche Problem mit @Produnis-Code ist, dass erzwungenes Kopieren stattfindet, da die Ergebnisvektoren bei jeder Iteration der Schleife erweitert werden.
Setzen Sie Monica - G. Simpson am
Schneefallspaket kann Gavins Lösung wie "Kuchen" erweitern. Das Paket verfügt über eine Vielzahl von Apply-Funktionen, die für Multicoring geändert wurden. Für die Apply-Funktion würden Sie sfApply verwenden (<Ihre Argumente für apply>). Schneefall ist auch gut dokumentiert. Ich sollte darauf hinweisen, dass keine zusätzliche Software erforderlich ist, um dies auf einem Multi-Core-Prozessor durchzuführen. Siehe stackoverflow.com/questions/4164960/... für ein sfLapply Beispiel.
Roman Luštrik