2009-07-25 12 views
24

Ho letto di Python multiprocessing module. Continuo a non pensare di avere una buona comprensione di cosa può fare.Python: elaborazione multicore?

Diciamo che ho un processore quadcore e ho una lista con 1.000.000 di interi e voglio la somma di tutti gli interi. Potrei semplicemente fare:

list_sum = sum(my_list) 

Ma questo lo invia solo ad un nucleo.

E 'possibile, utilizzando il modulo multiprocessing, dividere l'array e fare in modo che ogni core ottenga la somma della sua parte e restituisca il valore in modo che la somma totale possa essere calcolata?

Qualcosa di simile:

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 

Qualsiasi aiuto sarebbe apprezzato.

risposta

30

Sì, è possibile fare questo sommatoria di diversi processi, molto simile a farlo con più thread:

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() 

Tuttavia , è probabile che farlo con più processi sia più lento rispetto a farlo in un unico processo, poiché la copia dei dati avanti e indietro è più costosa della loro somma immediata.

+1

@ Martin, credo che questo deadlock, per http://docs.python.org/library/multiprocessing.html#multiprocessing-programming: "un processo che ha messo gli elementi in un la coda aspetterà prima di terminare finché tutti gli elementi memorizzati nel buffer non saranno alimentati dal thread "feeder" alla pipe sottostante "- l'esempio di deadlock che i documenti danno è molto simile al tuo codice (è un singolo sottoprocesso all'inizio, join, get sequenza) e due sottoprocessi invece di uno non aiutano. Scambia i join e ottiene, o semplicemente rimuovi i join. –

+0

"Ha funzionato per me", probabilmente perché i dati si inseriscono semplicemente nel tubo. In ogni caso, ho rimosso i join. –

+0

Lo stai facendo su Linux? – Nope

20

Benvenuti nel mondo della programmazione concorrente.

Ciò che Python può (e non può) fare dipende da due cose.

  1. Cosa il sistema operativo può (e non può) fare. La maggior parte dei sistemi operativi alloca processi ai core. Per utilizzare 4 core, è necessario suddividere il problema in quattro processi. Questo è più facile di quanto sembri. A volte.

  2. Cosa possono fare (e non possono) le librerie C sottostanti. Se le librerie C espongono le funzionalità del sistema operativo e il sistema operativo espone le funzionalità dell'hardware, sei solido.

Per interrompere un problema in più processi, in particolare in GNU/Linux, è facile. Spezzala in una pipeline a più passaggi.

Nel caso di sommare un milione di numeri, pensare al seguente script di shell. Supponendo alcuni ipotetici programmi sum.py che sommano un intervallo di numeri o un elenco di numeri su stdin.

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

Questo avrebbe 3 processi simultanei. Due stanno facendo somme di molti numeri, il terzo sta sommando due numeri.

Poiché le shell GNU/Linux e il sistema operativo gestiscono già alcune parti della concorrenza, è possibile progettare programmi semplici (molto, molto semplici) che leggono da stdin, scrivono su stdout e sono progettati per eseguire piccole parti di un grande lavoro.

È possibile provare a ridurre i costi generali utilizzando subprocess per creare la pipeline anziché allocare il lavoro alla shell. È possibile tuttavia notare che la shell crea pipeline molto, molto rapidamente. (. È stato scritto direttamente in C e rende le chiamate OS API dirette per voi)

+0

Ho sentito questa risposta ha mostrato un sacco di ingegnosità. Nessun problema in CS non può essere risolto semplicemente aggiungendo uno strato di riferimento indiretto. – earino

+0

@earino: OTOH, non ha risposto affatto alla domanda dell'OP, che riguardava specificamente "come si usa il modulo multiprocessing". –

+2

@ Martin v. Löwis: vero. IMO il problema più grande (utilizzando tutti i core) è spesso più importante della domanda posta (usando il sottoprocesso per usare tutti i core). In alcuni casi la domanda posta riflette un numero di ipotesi errate. –

7

Certo, ad esempio:

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() 

risultati in:

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

nota che questa granularità è troppo fine per essere processi di deposizione vale per - il compito generale sommatore è piccolo (che è perché posso ricalcolare la somma in main come un controllo ;-) e troppi dati vengono spostati avanti e indietro (in effetti i sottoprocessi non avrebbero bisogno di ottenere copie delle sottoliste su cui lavorano - gli indici sarebbero sufficienti). Quindi, è un "esempio di giocattolo" in cui la multiprocessing non è realmente giustificata. Con architetture diverse (utilizzare un pool di sottoprocessi che ricevono più attività da eseguire da una coda, ridurre al minimo il movimento dei dati avanti e indietro, ecc. Ecc.) E su attività meno dettagliate si potrebbero tuttavia ottenere benefici in termini di prestazioni.