2009-07-25 12 views
24

He estado leyendo sobre Python's multiprocessing module. Todavía no creo tener una buena comprensión de lo que puede hacer.Python: ¿Procesamiento multinúcleo?

Digamos que tengo un procesador quadcore y tengo una lista con 1,000,000 enteros y quiero la suma de todos los enteros. Simplemente podría hacer:

list_sum = sum(my_list) 

Pero esto solo lo envía a un núcleo.

¿Es posible, mediante el módulo de multiprocesamiento, dividir la matriz y hacer que cada núcleo obtenga la suma de su parte y devolver el valor para que se pueda calcular la suma total?

Algo así como:

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 

se agradecería cualquier ayuda.

Respuesta

30

Sí, es posible hacer esto suma sobre varios procesos, muy parecido a hacerlo con varios subprocesos:

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

Sin embargo , es probable que hacerlo con procesos múltiples sea probablemente más lento que hacerlo en un solo proceso, ya que copiar los datos y volver es más costoso que sumarlos de inmediato.

+1

@Martin, creo que estos puntos muertos, por http://docs.python.org/library/multiprocessing.html#multiprocessing-programming: "un proceso que ha puesto elementos en una la cola esperará antes de terminar hasta que todos los elementos almacenados en el búfer sean alimentados por el hilo "alimentador" al conducto subyacente "- el ejemplo de punto muerto que los documentos proporcionan es muy similar a su código (es un solo subproceso al inicio, unir, obtener secuencia) y dos subprocesos en lugar de uno no ayudan. Cambie las uniones y obtiene, o simplemente elimine las uniones. –

+0

"Funcionó para mí", probablemente porque los datos simplemente encajan en la tubería. En cualquier caso, he eliminado las uniones. –

+0

¿Está ejecutando esto en Linux? – Nope

20

Bienvenido al mundo de la programación simultánea.

Lo que Python puede (y no puede) hacer depende de dos cosas.

  1. Lo que el sistema operativo puede (y no puede) hacer. La mayoría de los sistemas operativos asignan procesos a núcleos. Para usar 4 núcleos, debe dividir su problema en cuatro procesos. Esto es más fácil de lo que parece. A veces.

  2. Lo que las bibliotecas C subyacentes pueden (y no pueden) hacer. Si las bibliotecas C exponen características del sistema operativo y el sistema operativo expone características del hardware, usted es sólido.

Para dividir un problema en múltiples procesos, especialmente en GNU/Linux, es fácil. Divídalo en una tubería de varios pasos.

En el caso de sumar un millón de números, piense en el siguiente script de shell. Asumiendo un hipotético programa sum.py que suma un rango de números o una lista de números en stdin.

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

Esto tendría 3 procesos concurrentes. Dos hacen sumas de muchos números, el tercero suma dos números.

Como las cubiertas de GNU/Linux y el sistema operativo ya manejan algunas partes de la concurrencia, puede diseñar programas simples (muy, muy simples) que lean stdin, escriba stdout y estén diseñados para hacer partes pequeñas de un gran trabajo.

Puede intentar reducir los gastos generales utilizando subprocess para construir la canalización en lugar de asignar el trabajo al shell. Sin embargo, puede encontrar que el caparazón construye tuberías muy, muy rápido. (. Fue escrito directamente en C y hace llamadas a la API de OS directos para usted)

+0

Sentí que esta respuesta mostraba mucho ingenio. No hay problema en CS no se puede resolver simplemente agregando una capa de indirección. – earino

+0

@earino: OTOH, no respondía a la pregunta del OP en absoluto, que era específicamente sobre "cómo utilizo el módulo de multiprocesamiento". –

+2

@Martin v. Löwis: Es cierto. IMO, el problema más grande (usar todos los núcleos) a menudo es más importante que la pregunta (usando un subproceso para usar todos los núcleos). En algunos casos, la pregunta formulada refleja una serie de supuestos pobres. –

7

Claro, por ejemplo:

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

resultados en:

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

nota que esta granularidad es demasiado fina para ser procesos de desove por valor de - la tarea general de suma es pequeña (que es por qué puedo recalcular la suma en main como un cheque ;-) y demasiados datos se mueven hacia adelante y hacia atrás (de hecho, los subprocesos no necesitarían obtener copias de las sublistas en las que trabajan, los índices serían suficientes). Entonces, es un "ejemplo de juguete" donde el multiprocesamiento no está realmente garantizado. Con diferentes arquitecturas (use un conjunto de subprocesos que reciben múltiples tareas para realizar desde una cola, minimice el movimiento de datos hacia atrás y hacia adelante, etc., etc.) y en tareas menos detalladas, en realidad podría obtener beneficios en términos de rendimiento.

Cuestiones relacionadas