Hay dos diferencias clave entre imap
/ imap_unordered
y map
/ map_async
:
- La forma en que consumen lo iterable que les pasa.
- La forma en que te devuelven el resultado.
map
consume 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.
imap
no 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. chunksize
Sin embargo, esto puede mitigarse pasando un argumento mayor que el predeterminado de 1.
La otra gran diferencia entre imap
/ imap_unordered
y map
/ map_async
es 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
, AsyncResult
se 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 map
sí (en map
realidad se implementa internamente como map_async(...).get()
). No hay forma de obtener resultados parciales; tienes el resultado completo o nada.
imap
y imap_unordered
ambos 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_unordered
lugar de p.imap
, verá:
3 (Time elapsed: 1s)
5 (Time elapsed: 3s)
7 (Time elapsed: 5s)
Si usa p.map
o 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_unordered
over map_async
son:
- Su iterable es lo suficientemente grande como para que convertirlo en una lista le haga quedarse / usar demasiada memoria.
- ¿Quieres ser capaz de iniciar el procesamiento de los resultados antes de todo de ellos se han completado.
apply
envía una única tarea a un proceso de trabajo y luego bloquea hasta que se completa.apply_async
envía una única tarea a un proceso de trabajo y luego devuelve inmediatamente unAsyncResult
objeto, que puede usarse para esperar a que la tarea finalice y recuperar el resultado.apply
se implementa simplemente llamandoapply_async(...).get()
Pool
documentación oficial en lugar de la aburrida existente .