2013-05-24 10 views
10

Sto cercando di utilizzare il pacchetto doParallel e foreach ma sto ottenendo una riduzione delle prestazioni utilizzando l'esempio di bootstrap nella guida trovata qui CRANpage.Tentativo di iniziare con doParallel e foreach ma nessun miglioramento

library(doParallel) 
library(foreach) 
registerDoParallel(3) 
x <- iris[which(iris[,5] != "setosa"), c(1,5)] 
trials <- 10000 
ptime <- system.time({ 
    r <- foreach(icount(trials), .combine=cbind) %dopar% { 
    ind <- sample(100, 100, replace=TRUE) 
    result1 <- glm(x[ind,2]~x[ind,1], family=binomial(logit)) 
    coefficients(result1) 
    } 
    })[3] 
ptime 

Questo esempio restituisce 56.87.

Quando cambio lo dopar a solo do per eseguirlo in sequenza anziché in parallelo, restituisce 36.65.

Se si esegue registerDoParallel(6), il tempo parallelo scende a 42.11 ma è ancora più lento di quello sequenziale. registerDoParallel(8) diventa 40.31 ancora peggio del sequenziale.

Se aumento trials a 100.000, la sequenza sequenziale prende 417.16 e l'esecuzione parallela con 3 lavoratori richiede 597.31. Con 6 lavoratori in parallelo ci vuole 425.85.

Il mio sistema è

  • Dell Optiplex 990

  • Windows 7 Professional a 64 bit

  • 16 GB di RAM

  • Intel i-7-2600 3.6GHz Quad nucleo con hyperthreading

Sto facendo qualcosa di sbagliato qui? Se faccio la cosa più inventata che io possa pensare (sostituendo il codice computazionale con Sys.sleep(1)), ottengo una riduzione effettiva strettamente proporzionata al numero di lavoratori. Mi chiedo perché l'esempio nella guida diminuisca le prestazioni per me mentre per loro ha accelerato le cose?

+6

Questa è una quasi-FAQ: testare con Sys.sleep() andava bene e mostra che l'impostazione dei thread richiede più tempo del calcolo. Cerca di aumentare la dimensione del problema, ad esempio il campione (10000) e vedrai un miglioramento. Tuttavia, la tua macchina ha effettivamente solo 4 core, quindi niente funziona oltre i 4 core. Non ho mai visto un effetto di hyperthreading (sotto Windows, e senza compilazioni speciali R) –

+0

@DieterMenne: Il tuo punto su questo essere FAQish è ben preso. Il fatto che l'esempio della guida non abbia prodotto un beneficio per me attraverso me. Avevi ragione che aumentare le dimensioni del campione mi avrebbe portato dove il parallelo in esecuzione era un miglioramento. Inoltre, grazie per la dritta sull'HT. Ho fatto un test con 4 vs 8 lavoratori ed era praticamente la stessa ora. –

risposta

9

Il problema sottostante è che doParallel esegue attach per ogni esecuzione di attività sui lavoratori del cluster PSOCK per aggiungere le variabili esportate al percorso di ricerca del pacchetto. Questo risolve diversi problemi di scoping, ma può danneggiare significativamente le prestazioni, in particolare con attività di breve durata e grandi quantità di dati esportati. Questo non si verifica su Linux e Mac OS X con il tuo esempio, poiché useranno mclapply, anziché clusterApplyLB, ma si verificherà su tutte le piattaforme se si registra esplicitamente un cluster PSOCK.

Credo di aver capito come risolvere i problemi di scoping dell'attività in modo diverso che non danneggia le prestazioni e sto lavorando con Revolution Analytics per ottenere la correzione nella prossima versione di doParallel e doSNOW , che ha anche lo stesso problema.

È possibile aggirare il problema utilizzando compito suddivisione in blocchi:

ptime2 <- system.time({ 
    chunks <- getDoParWorkers() 
    r <- foreach(n=idiv(trials, chunks=chunks), .combine='cbind') %dopar% { 
    y <- lapply(seq_len(n), function(i) { 
     ind <- sample(100, 100, replace=TRUE) 
     result1 <- glm(x[ind,2]~x[ind,1], family=binomial(logit)) 
     coefficients(result1) 
    }) 
    do.call('cbind', y) 
    } 
})[3] 

Questo si traduce in un solo compito per lavoratore, per cui ogni lavoratore esegue solo attach una volta, piuttosto che trials/3 volte.Ciò comporta anche meno operazioni di socket più grandi, che possono essere eseguite in modo più efficiente sulla maggior parte dei sistemi, ma in questo caso il problema critico è attach.

+0

con 4 lavoratori ci sono voluti 9,2 secondi. Quando ho eseguito blocchi = 1 ci sono voluti 28.18 –

+0

Stavo per inviare un'email al manutentore per farglielo sapere, ma è Revolution Analytics e non voglio essere colpito da venditori. –

+2

@DeanMacGregor Non ti preoccupare: li ho già contattati, soprattutto da quando ho scoperto qual è il vero problema di fondo e credo di averlo risolto. Sto lavorando con Revolution in questo momento per averlo corretto nella prossima versione di doSNOW e doParallel. –