2016-07-06 12 views
10

Testuję funkcję parLapplyLB(), aby zrozumieć, co robi, aby zrównoważyć obciążenie. Ale nie widzę żadnej równowagi. Na przykład,Dlaczego parLapplyLB tak naprawdę nie równoważą obciążenia?

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) 

Jeśli to było naprawdę równoważenie obciążenia, pierwszej pracy (Job 1), który śpi na 3 sekundy będzie na pierwszym węźle i pozostałych trzech miejsc pracy (Praca 2: 4) będzie spać w sumie 1,5 sekundy na drugim węźle. Łącznie czas systemowy powinien wynosić tylko 3 sekundy.

Zamiast tego, myślę, że zadania 1 i 2 są przydzielane węzłowi 1, a zadania 3 i 4 są przydzielane węzłowi 2. W rezultacie całkowity czas wynosi 3 + 0,5 = 3,5 sekundy. Jeśli uruchomimy ten sam kod powyżej z parLapply() zamiast parLapplyLB(), otrzymamy ten sam czas systemowy około 3,5 sekundy.

Czego nie rozumiem lub nie robię źle?

+0

Myślę, że R nie wykonuje automatycznego równoważenia obciążenia. Myślę, że dzieli on * zadania * na tak wiele rdzeni, jakie są dostępne, niezależnie od czasu potrzebnego na wykonanie każdego zadania lub zakończenia każdego zadania. To nie jest kolejka zadań, a kiedy jeden robotnik skończył, chwyta następny. Każdemu rdzeniu przypisano dwa zadania. Stąd 3 + 0,5 na pierwszego pracownika, a łącznie 3,5. * byłby szczęśliwy, gdyby się mylił * – gregmacfarlane

+3

Tak, to stąd pochodzi 3.5. To nie równoważenie obciążenia. Ale parLapplyLB twierdzi, że balansuje. – josiekre

Odpowiedz

10

dla zadania jak twoje (i, z tego względu, dla każdego zadania, za które ja kiedykolwiek potrzebne równoległego) parLapplyLB naprawdę nie jest odpowiednim narzędziem do pracy. Aby zrozumieć, dlaczego nie spojrzeć na sposób, w jaki to projekt:

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 

Problemem jest to, że najpierw rozdziela swoją listę zadań się na równe wielkości podlist że następnie dystrybuuje wśród węzłów, z których każdy uruchamia lapply() na swojej podanej liście. Tutaj pierwszy węzeł wykonuje zadania na pierwszym i drugim wejściu, podczas gdy drugi węzeł wykonuje zadania za pomocą trzeciego i czwartego wejścia.

Zamiast używać bardziej wszechstronny clusterApplyLB(), która działa tak jak chcesz nadzieję:

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

Dzięki! Tego właśnie szukałem. Nie mogę wymyślić przypadku, w którym parLapplyLB będzie produkować coś innego niż parLapply, a więc nie jestem pewien, jaki jest jego cel. – josiekre

+0

Czy 'clusterApplyLB (cl, X, fun)' ma takie samo zamierzone działanie jak 'parLapplyLB'? Próbowałem tego w moim systemie, i wydaje się, że daje ten sam wynik, gdy 'X' jest listą, ale trochę się denerwuję po prostu zamieniając' parLapplyLB' na 'clusterApplyLB' .. – guy

+2

przydatne informacje tutaj , jak również zdefiniowany przez użytkownika parlapplyLB http://detritus.fundacioace.com/pub/books/Oreilly.Parallel.R.Oct.2011.pdf – Olivia

1

parLapplyLB nie jest równoważenie obciążenia, ponieważ ma błąd semantyczny. Znaleźliśmy błąd i podaliśmy poprawkę, zobacz here. Teraz, aż do programistów R, aby załączyć poprawkę.

Powiązane problemy