2016-07-06 19 views
10

Sto testando la funzione parLapplyLB() per capire cosa fa per bilanciare un carico. Ma non vedo alcun evento di bilanciamento. Ad esempio,Perché parLapplyLB non bilancia effettivamente il carico?

cl <- parallel::makeCluster(2) 

system.time(
    parallel::parLapplyLB(cl, 1:4, function(y) { 
    if (y == 1) { 
     Sys.sleep(3) 
    } else { 
     Sys.sleep(0.5) 
    }})) 
## user system elapsed 
## 0.004 0.009 3.511 

parallel::stopCluster(cl) 

Se è stato veramente il bilanciamento del carico, il primo lavoro (job 1) che dorme per 3 secondi sarebbe sul primo nodo e gli altri tre posti di lavoro (posti di lavoro 2: 4) sarebbe dormire per un totale di 1,5 secondi sull'altro nodo. In totale, il tempo di sistema dovrebbe essere solo 3 secondi.

Invece, penso che i lavori 1 e 2 siano assegnati al nodo 1 e i lavori 3 e 4 siano assegnati al nodo 2. Ne risulta che il tempo totale è 3 + 0,5 = 3,5 secondi. Se eseguiamo lo stesso codice sopra con parLapply() anziché parLapplyLB(), otteniamo lo stesso tempo di sistema di circa 3,5 secondi.

Cosa non sto capendo o storto?

+0

Penso che R non esegua il bilanciamento automatico del carico. Penso che divida le * attività * su tutti i core disponibili, indipendentemente dal tempo necessario per eseguire ogni attività o quando ogni attività viene completata. Non è come se ci fosse una coda di compiti, e quando un lavoratore ha finito, afferra il prossimo. A ciascun core sono stati assegnati due compiti. Quindi 3 + 0,5 sul primo operatore e un totale di 3,5. * Sarebbe felice di sbagliare * – gregmacfarlane

+3

Sì, ecco da dove viene il 3.5. Non sta equilibrando il carico. Ma la parLapplyLB afferma di bilanciare. – josiekre

risposta

10

Per un compito come la tua (e, del resto, per qualsiasi compito per il quale io abbia mai avuto bisogno parallelo) parLapplyLB in realtà non è lo strumento giusto per il lavoro. Per vedere perché no, avere uno sguardo al modo in cui è implementata:

parLapplyLB 
# function (cl = NULL, X, fun, ...) 
# { 
#  cl <- defaultCluster(cl) 
#  do.call(c, clusterApplyLB(cl, x = splitList(X, length(cl)), 
#   fun = lapply, fun, ...), quote = TRUE) 
# } 
# <bytecode: 0x000000000f20a7e8> 
# <environment: namespace:parallel> 

## Have a look at what `splitList()` does: 
parallel:::splitList(1:4, 2) 
# [[1]] 
# [1] 1 2 
# 
# [[2]] 
# [1] 3 4 

Il problema è che prima si divide la sua lista di posti di lavoro fino in sottoliste uguali dimensioni che distribuisce poi tra i nodi, ciascuno dei quali corre lapply() sulla sua sottolista data. Quindi, qui, il primo nodo esegue i lavori sul primo e sul secondo input, mentre il secondo nodo esegue i lavori utilizzando il terzo e il quarto input.

Al contrario, utilizzare il più versatile clusterApplyLB(), che funziona esattamente come ci si spera:

system.time(
    parallel::clusterApplyLB(cl, 1:4, function(y) { 
    if (y == 1) { 
     Sys.sleep(3) 
    } else { 
     Sys.sleep(0.5) 
    }})) 
# user system elapsed 
# 0.00 0.00 3.09 
+0

Grazie! Quello che stavo cercando. Non riesco a pensare a un caso in cui parLapplyLB produrrebbe qualcosa di diverso da parLapply, e quindi non sono sicuro di quale sia il suo scopo. – josiekre

+0

'clusterApplyLB (cl, X, fun)' ha lo stesso comportamento previsto di 'parLapplyLB'? Ho provato questo sul mio sistema, e sembra dare lo stesso risultato quando 'X' è una lista, ma sono un po 'nervoso semplicemente scambiando' parLapplyLB' con 'clusterApplyLB' .. – guy

+2

informazioni utili qui , così come utente definedarilypplyLB http://detritus.fundacioace.com/pub/books/Oreilly.Parallel.R.Oct.2011.pdf – Olivia

1

parLapplyLB non è il bilanciamento del carico perché ha un bug semantica. Abbiamo trovato il bug e fornito una correzione, vedere here. Ora, fino agli sviluppatori di R per includere la correzione.