2009-07-25 9 views
24

Czytałem o Pythonie multiprocessing module. Nadal nie sądzę, abym miał bardzo dobre zrozumienie tego, co potrafi.Python: przetwarzanie wielordzeniowe?

Załóżmy, że mam procesor czterordzeniowy i mam listę zawierającą 1 000 000 liczb całkowitych i chcę sumę wszystkich liczb całkowitych. Mogę po prostu:

list_sum = sum(my_list) 

Ale to tylko przesyła je do jednego rdzenia.

Czy jest możliwe, aby za pomocą modułu wieloprocesowego podzielić macierz i otrzymać każdy rdzeń, otrzymując sumę jego części i zwrócić wartość, aby można było obliczyć całkowitą sumę?

Coś jak:

core1_sum = sum(my_list[0:500000])   #goes to core 1 
core2_sum = sum(my_list[500001:1000000]) #goes to core 2 
all_core_sum = core1_sum + core2_sum  #core 3 does final computation 

Każda pomoc będzie mile widziane.

Odpowiedz

30

Tak, jest to możliwe do zrobienia tego sumowanie ciągu kilku procesów, bardzo podobnie jak robi to z wielu wątków:

from multiprocessing import Process, Queue 

def do_sum(q,l): 
    q.put(sum(l)) 

def main(): 
    my_list = range(1000000) 

    q = Queue() 

    p1 = Process(target=do_sum, args=(q,my_list[:500000])) 
    p2 = Process(target=do_sum, args=(q,my_list[500000:])) 
    p1.start() 
    p2.start() 
    r1 = q.get() 
    r2 = q.get() 
    print r1+r2 

if __name__=='__main__': 
    main() 

jednak , jest prawdopodobne, że robienie tego za pomocą wielu procesów jest wolniejsze niż robienie tego w jednym procesie, ponieważ kopiowanie danych z powrotem iz powrotem jest droższe niż ich natychmiastowe podsumowywanie.

+1

@Martin, Wierzę, że te zakleszczenia, za http://docs.python.org/library/multiprocessing.html#multiprocessing-programming: "proces, który umieścił przedmioty w kolejka będzie czekała przed zakończeniem, dopóki wszystkie buforowane elementy nie zostaną dostarczone przez wątek "podajnika" do podstawowej pętli "- przykład impasu, który daje docs jest bardzo podobny do twojego kodu (jest to pojedynczy podproces na początku, dołącz, pobierz sekwencja) i dwa podprocesy zamiast jednego nie pomagają. Zamień wiązania i uzyskaj lub po prostu usuń połączenia. –

+0

"Udało się to dla mnie", prawdopodobnie dlatego, że dane po prostu pasują do rury. W każdym razie usunąłem łączenia. –

+0

Czy używasz tego w systemie Linux? – Nope

20

Witamy w świecie programowania współbieżnego.

Co Python może (i nie może) zrobić, zależy od dwóch rzeczy.

  1. Co system operacyjny może (i nie może) zrobić. Większość systemów operacyjnych alokuje procesy do rdzeni. Aby użyć 4 rdzeni, musisz podzielić swój problem na cztery procesy. To jest łatwiejsze niż się wydaje. Czasami.

  2. Co podstawowe biblioteki C mogą (i nie mogą) zrobić. Jeśli biblioteki C odsłaniają cechy systemu operacyjnego, a system operacyjny ujawnia funkcje sprzętu, jesteś solidny.

Złamanie problemu w wielu procesach - szczególnie w systemie GNU/Linux - jest łatwe. Podziel go na wieloetapowy potok.

W przypadku sumowania miliona liczb pomyśl o następującym skrypcie powłoki. Zakładając pewien hipotetyczny program sum.py, który sumuje zakres liczb lub listę liczb na stdin.

(suma 0 500000 & suma 50000 1000000) | sum.py

Miałoby to 3 jednoczesne procesy. Dwa robią sumy wielu liczb, trzecia to suma dwóch liczb.

Ponieważ powłoki GNU/Linux i system operacyjny obsługują już niektóre elementy współbieżności, możesz zaprojektować proste (bardzo, bardzo proste) programy, które czytają ze standardowego wejścia, piszą na standardowe wyjście i są zaprojektowane do wykonywania małych części duża praca.

Możesz spróbować zmniejszyć narzuty, używając subprocess do zbudowania potoku zamiast przydzielania zadania do powłoki. Może się jednak okazać, że powłoka buduje rurociągi bardzo, bardzo szybko. (. Został on napisany bezpośrednio w C i sprawia, że ​​bezpośrednie rozmowy OS API dla ciebie)

+0

Czułem, że ta odpowiedź wykazała się dużą pomysłowością. Żaden problem w CS nie może zostać rozwiązany przez dodanie warstwy pośredniej. – earino

+0

@earino: OTOH, nie odpowiedział na pytanie OP w ogóle, co było konkretnie o "jak używać modułu wieloprocesowego". –

+2

@Martin v. Löwis: To prawda. IMO większy problem (przy użyciu wszystkich rdzeni) jest często ważniejszy niż pytanie zadane (używając podprocesu do użycia wszystkich rdzeni). W niektórych przypadkach postawione pytanie odzwierciedla szereg słabych założeń. –

7

Jasne, na przykład:

from multiprocessing import Process, Queue 

thelist = range(1000*1000) 

def f(q, sublist): 
    q.put(sum(sublist)) 

def main(): 
    start = 0 
    chunk = 500*1000 
    queue = Queue() 
    NP = 0 
    subprocesses = [] 
    while start < len(thelist): 
     p = Process(target=f, args=(queue, thelist[start:start+chunk])) 
     NP += 1 
     print 'delegated %s:%s to subprocess %s' % (start, start+chunk, NP) 
     p.start() 
     start += chunk 
     subprocesses.append(p) 
    total = 0 
    for i in range(NP): 
     total += queue.get() 
    print "total is", total, '=', sum(thelist) 
    while subprocesses: 
     subprocesses.pop().join() 

if __name__ == '__main__': 
    main() 

skutkuje:

$ python2.6 mup.py 
delegated 0:500000 to subprocess 1 
delegated 500000:1000000 to subprocess 2 
total is 499999500000 = 499999500000 

zauważyć, że ta szczegółowość jest zbyt dobrze, aby być warte procesy tarła - ogólnego zadania sumującego jest mały (co jest dlaczego mogę przeliczyć główną sumę jako czek ;-) i zbyt wiele danych jest przenoszonych tam i z powrotem (w rzeczywistości podprocesy nie musiałyby pobierać kopii podlist, nad którymi pracują - wystarczyłyby indeksy). Jest to więc "zabawny przykład", w którym przetwarzanie wieloprocesowe nie jest tak naprawdę uzasadnione. Z różnymi architekturami (użyj puli podprocesów, które otrzymują wiele zadań do wykonania z kolejki, zminimalizuj przesuwanie danych w przód iw tył, itp.) I mniej szczegółowych zadań, które mogłyby jednak przynieść korzyści pod względem wydajności.