multiprocesamiento.Piscina: ¿Cuál es la diferencia entre map_async e imap?

184

Estoy tratando de aprender a usar el multiprocessingpaquete de Python , pero no entiendo la diferencia entre map_asyncy imap. Me di cuenta de que ambos map_asyncy imapse ejecutan de forma asincrónica. Entonces, ¿cuándo debo usar uno sobre el otro? ¿Y cómo debo recuperar el resultado devuelto por map_async?

¿Debo usar algo como esto?

def test():
    result = pool.map_async()
    pool.close()
    pool.join()
    return result.get()

result=test()
for i in result:
    print i
espacio
fuente

Respuestas:

492

Hay dos diferencias clave entre imap/ imap_unorderedy map/ map_async:

  1. La forma en que consumen lo iterable que les pasa.
  2. La forma en que te devuelven el resultado.

mapconsume tu iterable convirtiéndolo en una lista (suponiendo que ya no sea una lista), dividiéndolo en trozos y enviando esos trozos a los procesos de trabajo en el Pool. Romper el iterable en trozos funciona mejor que pasar cada elemento en el iterable entre procesos, un elemento a la vez, particularmente si el iterable es grande. Sin embargo, convertir el iterable en una lista para fragmentarlo puede tener un costo de memoria muy alto, ya que toda la lista deberá mantenerse en la memoria.

imapno convierte el iterable que le da en una lista, ni lo divide en trozos (por defecto). Iterará sobre el elemento iterable de uno en uno y los enviará a un proceso de trabajo. Esto significa que no tiene el golpe de memoria de convertir todo el iterable en una lista, pero también significa que el rendimiento es más lento para iterables grandes, debido a la falta de fragmentación. chunksizeSin embargo, esto puede mitigarse pasando un argumento mayor que el predeterminado de 1.

La otra gran diferencia entre imap/ imap_unorderedy map/ map_asynces que con imap/ imap_unordered, puede comenzar a recibir resultados de los trabajadores tan pronto como estén listos, en lugar de tener que esperar a que todos terminen. Con map_async, AsyncResultse devuelve de inmediato, pero en realidad no puede recuperar resultados de ese objeto hasta que todos hayan sido procesados, en cuyo momento devuelve la misma lista que mapsí (en maprealidad se implementa internamente como map_async(...).get()). No hay forma de obtener resultados parciales; tienes el resultado completo o nada.

imapy imap_unorderedambos devuelven iterables de inmediato. Con imap, los resultados se obtendrán del iterable tan pronto como estén listos, mientras se conserva el orden del iterable de entrada. Con imap_unordered, los resultados se producirán tan pronto como estén listos, independientemente del orden de la entrada iterable. Entonces, digamos que tienes esto:

import multiprocessing
import time

def func(x):
    time.sleep(x)
    return x + 2

if __name__ == "__main__":    
    p = multiprocessing.Pool()
    start = time.time()
    for x in p.imap(func, [1,5,3]):
        print("{} (Time elapsed: {}s)".format(x, int(time.time() - start)))

Esto generará:

3 (Time elapsed: 1s)
7 (Time elapsed: 5s)
5 (Time elapsed: 5s)

Si usa en p.imap_unorderedlugar de p.imap, verá:

3 (Time elapsed: 1s)
5 (Time elapsed: 3s)
7 (Time elapsed: 5s)

Si usa p.mapo p.map_async().get(), verá:

3 (Time elapsed: 5s)
7 (Time elapsed: 5s)
5 (Time elapsed: 5s)

Entonces, las razones principales para usar imap/ imap_unorderedover map_asyncson:

  1. Su iterable es lo suficientemente grande como para que convertirlo en una lista le haga quedarse / usar demasiada memoria.
  2. ¿Quieres ser capaz de iniciar el procesamiento de los resultados antes de todo de ellos se han completado.
dano
fuente
1
¿Qué pasa con apply y apply_async?
Harf Daftary
10
@HarshDaftary applyenvía una única tarea a un proceso de trabajo y luego bloquea hasta que se completa. apply_asyncenvía una única tarea a un proceso de trabajo y luego devuelve inmediatamente un AsyncResultobjeto, que puede usarse para esperar a que la tarea finalice y recuperar el resultado. applyse implementa simplemente llamandoapply_async(...).get()
dano
51
Ese es el tipo de descripción que debería estar en la Pooldocumentación oficial en lugar de la aburrida existente .
minutos
@dano Quiero ejecutar una función en segundo plano, pero tengo algunas limitaciones de recursos y no puedo ejecutar la función tantas veces como quiero y quiero poner en cola las ejecuciones adicionales de la función. ¿Tienes alguna idea de cómo debo hacer eso? Tengo mi pregunta aquí . ¿Podrías echar un vistazo a mi pregunta y ver si puedes darme algunas pistas (o mejor aún, una respuesta) sobre cómo debo hacer eso?
Amir
1
@BallpointBen Pasará al siguiente trabajo tan pronto como termine. Los pedidos se manejan nuevamente en el proceso padre.
dano