2012-02-20 49 views
6

Tengo un bucle anidado para en mi código Python que se ve algo como esto:paralelizar anidado de bucle en IPython

results = [] 

for azimuth in azimuths: 
    for zenith in zeniths: 
     # Do various bits of stuff 
     # Eventually get a result 
     results.append(result) 

me gustaría paralelizar este bucle en mi máquina de 4 núcleos para acelerarlo . Mirando la documentación de programación paralela de IPython (http://ipython.org/ipython-doc/dev/parallel/parallel_multiengine.html#quick-and-easy-parallelism) parece que hay una manera fácil de usar map para paralelizar iterativo operaciones.

Sin embargo, para hacer eso necesito tener el código dentro del bucle como una función (que es fácil de hacer), y luego mapear a través de esta función. El problema que tengo es que no puedo obtener una matriz para mapear esta función. itertools.product() produce un iterador con el que no puedo usar la función de mapa.

¿Estoy ladrando al árbol equivocado tratando de usar el mapa aquí? Hay una mejor manera de hacerlo? ¿O hay alguna forma de usar itertools.product y luego ejecutar en paralelo con una función mapeada a través de los resultados?

+0

función de mapa de Clojure funciona a través de secuencias, y aunque la mayoría de secuencias son contenedores, es realmente sólo una interfaz que cualquier cosa puede poner en práctica. ¿Hay algo así que puedas aprovechar en (hierro) Python? – Bill

+0

¿Qué sucede cuando pruebas 'itertools.product'? Esa parece ser la forma de hacerlo. –

+0

'itertools.product' parece ser ideal para crear un iterador sobre todas las combinaciones de azimuts y cenit, pero parece que no puedo paralelizar esto. ¿Alguna idea de como hacer esto? – robintw

Respuesta

10

Para poner en paralelo cada llamada, solo necesita obtener una lista para cada argumento. Puede utilizar itertools.product + zip para conseguir esto:

allzeniths, allazimuths = zip(*itertools.product(zeniths, azimuths)) 

continuación, puede utilizar el mapa:

amr = dview.map(f, allzeniths, allazimuths) 

Para ir un poco más en los pasos, aquí está un ejemplo:

zeniths = range(1,4) 
azimuths = range(6,8) 

product = list(itertools.product(zeniths, azimuths)) 
# [(1, 6), (1, 7), (2, 6), (2, 7), (3, 6), (3, 7)] 

Entonces, tenemos una "lista de pares", pero lo que realmente queremos es una lista única para cada argumento, es decir, un "par de listas". Esto es exactamente lo que la sintaxis poco raro zip(*product) nos obtiene:

allzeniths, allazimuths = zip(*itertools.product(zeniths, azimuths)) 

print allzeniths 
# (1, 1, 2, 2, 3, 3) 
print allazimuths 
# (6, 7, 6, 7, 6, 7) 

Ahora sólo mapear nuestra función en esas dos listas, poner en paralelo bucles for anidados:

def f(z,a): 
    return z*a 

view.map(f, allzeniths, allazimuths) 

Y no hay nada especial acerca de que hay solo dos: este método debe extenderse a un número arbitrario de bucles anidados.

1

No estoy muy familiarizado con IPython, pero una solución fácil parecería ser la paralelización solo del bucle externo.

def f(azimuth): 
    results = [] 
    for zenith in zeniths: 
     #compute result 
     results.append(result) 
    return results 

allresults = map(f, azimuths) 
8

Supongo que está utilizando IPython 0.11 o posterior. Primero que todo defina una función simple.

def foo(azimuth, zenith): 
    # Do various bits of stuff 
    # Eventually get a result 
    return result 

luego usar paquete paralelo fina de IPython para paralelizar su problema. primero iniciar un controlador con 5 motores unidos (#CPUs + 1) iniciando un cluster en una ventana de terminal (si ha instalado IPython 0,11 o temprano este programa debe estar presente):

ipcluster start -n 5 

En la secuencia de comandos se conectan a la controlador y transmitir todas sus tareas. El controlador se encargará de todo.

from IPython.parallel import Client 

c = Client() # here is where the client establishes the connection 
lv = c.load_balanced_view() # this object represents the engines (workers) 

tasks = [] 
for azimuth in azimuths: 
    for zenith in zeniths: 
     tasks.append(lv.apply(foo, azimuth, zenith)) 

result = [task.get() for task in tasks] # blocks until all results are back 
+0

Con este ejemplo, obtiene un error arrojado sobre el resultado global no definido. Supongo que los procesadores múltiples no están viendo el resultado. Cómo compartir? –

+0

@ john-m Lo escribí hace 4 años. En este momento, IPython estaba en la versión 0.11. Están en 4.x ahora, creo. Mi ejemplo podría estar desactualizado. Además, no he usado IPython durante años. – koloman

1

Si realmente se desea ejecutar su código en paralelo, utilice concurrent.futures

import itertools 
import concurrent.futures 

def _work_horse(azimuth, zenith): 
    #DO HEAVY WORK HERE 
    return result 

futures = [] 
with concurrent.futures.ProcessPoolExecutor() as executor: 
    for arg_set in itertools.product(zeniths, azimuths): 
     futures.append(executor.submit(_work_horse, *arg_set)) 
executor.shutdown(wait=True) 

# Will time out after one hour. 
results = [future.result(3600) for future in futures] 
Cuestiones relacionadas