Come ottimizzare il mio script R per utilizzare il "multicore"


15

Sto usando GNU R su un PC Ubuntu-Lucid che ha 4 CPU. Per utilizzare tutte e 4 le CPU, ho installato il pacchetto "r-cran-multicore". Dato che nel manuale del pacchetto mancano esempi pratici che capisco, ho bisogno di consigli su come ottimizzare il mio script al fine di utilizzare tutte e 4 le CPU.

Il mio set di dati è un data.frame (chiamato P1) con 50.000 righe e 1600 cols. Per ogni riga, vorrei calcolare il massimo, la somma e la media. La mia sceneggiatura ha il seguente aspetto:

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

Qualcuno potrebbe dirmi come modificare ed eseguire lo script per utilizzare tutte e 4 le CPU?


c'è un errore nel programma sopra: la linea dovrebbe essere "per (i in 1: forza)"
Simon Byrne,

sei giusto, grazie!
Produnis,

1
questo non appartiene a StackOverflow?
R_Coholic

1
Questo appartiene a StackOverflow. Non ci sono domande statistiche qui. Solo una domanda di programmazione generale.
JD Long,

Risposte:


11

Usa foreach e doMC . La spiegazione dettagliata può essere trovata qui . La tua sceneggiatura cambierà molto poco, la linea

for(i in 1:plength){

dovrebbe essere cambiato in

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

I prerequisiti per qualsiasi script multitasking che utilizza questi pacchetti sono

library(foreach)
library(doMC)
registerDoMC()

Nota di attenzione. Secondo la documentazione non è possibile utilizzarlo nella GUI.

Per quanto riguarda il tuo problema, hai davvero bisogno del multitasking? Il tuo data.frame richiede circa 1,2 GB di RAM, quindi dovrebbe adattarsi alla tua memoria. Quindi puoi semplicemente usare applicare:

p1smry <- apply(P1,1,summary)

Il risultato sarà una matrice con i riassunti di ogni riga.

Puoi anche usare la funzione mclapply che si trova nel pacchetto multicore. Quindi il tuo script potrebbe apparire così:

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

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

Ciò restituirà l'elenco, in cui l'i-esimo elemento sarà il riepilogo dell'i-esima riga. Puoi convertirlo in matrice usando sapply

mres <- sapply(res,function(x)x)

Grazie mille. Hai ragione, che con "applica" lo script potrebbe essere ottimizzato. Ho appena usato la mia sceneggiatura come esempio minimo per far passare il messaggio ... Grazie mille, la tua risposta è esattamente quello che stavo cercando !!
Produnis,

15

Hai già una risposta su come utilizzare più di un core, ma il vero problema è con il modo in cui hai scritto i tuoi loop. Non estendere mai il vettore / oggetto del risultato ad ogni iterazione di un ciclo . Se lo fai, imponi a R di copiare il tuo vettore / oggetto risultato e di estenderlo, il che richiede tempo. Invece, preallocate abbastanza spazio di archiviazione prima di avviare il ciclo e compilare mentre procedete. Ecco un esempio:

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

Oppure puoi fare queste cose tramite 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 

Ma nota che questo non è più veloce del fare il loop correttamente e talvolta più lentamente.

Tuttavia, cerca sempre il codice vettoriale. Puoi fare somme di riga e significa usare rowSums()e rowMeans()che sono più veloci del ciclo o delle applyversioni:

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

   user  system elapsed 
  0.001   0.000   0.002 

Se fossi un uomo di scommesse, avrei denaro sul terzo approccio che menziono battendo foreach()o le altre opzioni multi-core in un test di velocità sulla tua matrice perché dovrebbero accelerare notevolmente le cose per giustificare il sovraccarico sostenuto nell'impostare il processi separati che vengono estrapolati dai diversi core della CPU.

Aggiornamento: seguendo il commento di @shabbychef è più veloce fare le somme una volta e riutilizzarle nel calcolo della media?

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

   user  system elapsed 
  0.002   0.000   0.002

Non in questo test, ma questo è lungi dall'essere esaustivo ...


FWIW, Matlab ha gli stessi problemi relativi alla preallocazione e ai vettori in espansione, ed è un classico 'blooper' di codice. Oltre alla tua scommessa, è probabilmente più veloce usare i risultati di rowSumscalcolare i mezzi della riga (a meno che non mi manchi qualcosa riguardo ad esempio Na o NaN). Il codice nel tuo terzo approccio somma ogni colonna due volte .
Shabbychef,

@shabbychef rimarrai sorpreso (vedi la mia risposta modificata). Sì, le somme vengono calcolate teoricamente due volte, ma rowSumse rowMeanssono codice compilato altamente ottimizzato e ciò che guadagniamo nel calcolare le somme una sola volta, perdiamo di nuovo nel fare il calcolo medio nel codice interpretato.
Ripristina Monica - G. Simpson,

@Gavin Simpson: non così veloce: prova invece system.time({ for (iii in c(1:1000)) { p1max3 <- apply(p1, 1, max) p1mean3 <- rowMeans(p1) p1sum3 <- rowSums(p1) } })e allo stesso modo system.time({ for (iii in c(1:1000)) { p1max4 <- apply(p1, 1, max) p1sum4 <- rowSums(p1) p1mean4 <- p1sum4 / ncol(p1) } }); la versione che non ricalcola la somma impiega 1.368 secondi sul mio computer; quello che fa prende 1.396. di nuovo, tutt'altro che esaustivo, ma più avvincente ...
shabbychef,

@shabbychef dobbiamo avere idee diverse su ciò che è o non è convincente ;-) In effetti, le tue simulazioni più rigorose rafforzano il mio punto principale, che come rowMeanse rowSumssono implementate in un codice compilato efficiente e ottimizzato, saranno difficili da battere.
Ripristina Monica - G. Simpson,

@Gavin Simpson. In realtà, il problema con il mio esempio è che la maggior parte del tempo viene impiegato nella parte di applicazione per calcolare il massimo. Sono d'accordo con te sul fatto che una funzione vettorizzata basata su c come rowMeansarà difficile da battere tramite uno strumento R di uso generale come *apply. Tuttavia, sembra suggerire che sia più veloce sommare 10000 numeri due volte tramite rowMeane rowSumanziché solo una volta e utilizzare l'operatore di divisione incorporato di R. So che R ha alcuni problemi di efficienza ( ad esempio la recente scoperta delle parentesi graffe rispetto al problema tra parentesi), ma sembra folle.
shabbychef,

1

Dai un'occhiata ai pacchetti neve e nevicate . Un sacco di esempi con quelli ...

Se vuoi velocizzare quel codice specifico piuttosto che imparare a conoscere R e il parallelismo, dovresti farlo

P1 = matrix(rnorm(1000), ncol=10, nrow=10
apply(P1, 1, max)
apply(P1, 1, mean)
apply(P1, 1, sum)

per favore aiutatemi a modificare la mia sceneggiatura ...
Produnis,

2
Ti stanno solo nascondendo il giro. Il vero problema con il codice @Produnis è che la copia forzata continua perché i vettori dei risultati vengono estesi ad ogni iterazione del ciclo.
Ripristina Monica - G. Simpson,

pacchetto nevicate può estendere la soluzione di Gavin come dire "torta". Il pacchetto ha una pletora di funzione apply modificata per eseguire il multicoring. Per applicare la funzione, dovresti usare sfApply (<yourarguments as for apply>). Anche le nevicate sono ben documentate. Devo sottolineare che non è necessario alcun software aggiuntivo per eseguire ciò su un processore multi-core. Vedere stackoverflow.com/questions/4164960/... per un esempio di sfLapply.
Roman Luštrik,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.