Il existe deux différences clés entre imap
/ imap_unordered
et map
/ map_async
:
- La façon dont ils consomment l'itérable que vous leur transmettez.
- La façon dont ils vous renvoient le résultat.
map
consomme votre itérable en convertissant l'itérable en liste (en supposant que ce n'est pas déjà une liste), en le divisant en morceaux et en envoyant ces morceaux aux processus de travail dans le Pool
. La division de l'itérable en morceaux est plus efficace que le passage de chaque élément de l'itérable entre les processus, un élément à la fois, en particulier si l'itérable est volumineux. Cependant, transformer l'itérable en liste afin de le fragmenter peut avoir un coût de mémoire très élevé, car la liste entière devra être conservée en mémoire.
imap
ne transforme pas l'itérable que vous lui donnez en liste, ni ne le divise en morceaux (par défaut). Il itérera sur l'élément itérable un par un, et les enverra chacun à un processus de travail. Cela signifie que vous ne prenez pas le coup de mémoire de la conversion de l'ensemble de l'itérable en liste, mais cela signifie également que les performances sont plus lentes pour les grands itérables, en raison du manque de segmentation. Cela peut être atténué en passant un chunksize
argument plus grand que la valeur par défaut de 1, cependant.
L'autre différence majeure entre imap
/ imap_unordered
et map
/ map_async
, c'est qu'avec imap
/ imap_unordered
, vous pouvez commencer à recevoir les résultats des travailleurs dès qu'ils sont prêts, plutôt que d'avoir à attendre qu'ils soient tous terminés. Avec map_async
, an AsyncResult
est renvoyé tout de suite, mais vous ne pouvez pas réellement récupérer les résultats de cet objet tant qu'ils n'ont pas tous été traités, auquel cas il renvoie la même liste que map
( map
est en fait implémentée en interne map_async(...).get()
). Il n'y a aucun moyen d'obtenir des résultats partiels; soit vous avez le résultat complet, soit rien.
imap
et les imap_unordered
deux renvoient les itérables immédiatement. Avec imap
, les résultats seront générés à partir de l'itérable dès qu'ils sont prêts, tout en préservant l'ordre de l'itérable d'entrée. Avec imap_unordered
, les résultats seront générés dès qu'ils seront prêts, quel que soit l'ordre de l'itération d'entrée. Alors, dites que vous avez ceci:
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)))
Cela produira:
3 (Time elapsed: 1s)
7 (Time elapsed: 5s)
5 (Time elapsed: 5s)
Si vous utilisez à la p.imap_unordered
place de p.imap
, vous verrez:
3 (Time elapsed: 1s)
5 (Time elapsed: 3s)
7 (Time elapsed: 5s)
Si vous utilisez p.map
ou p.map_async().get()
, vous verrez:
3 (Time elapsed: 5s)
7 (Time elapsed: 5s)
5 (Time elapsed: 5s)
Ainsi, les principales raisons d'utiliser imap
/ imap_unordered
over map_async
sont:
- Votre itérable est suffisamment volumineux pour que le convertir en liste vous fasse manquer / utiliser trop de mémoire.
- Vous voulez être en mesure de commencer à traiter les résultats avant tout d'entre eux sont terminés.
apply
envoie une seule tâche à un processus de travail, puis se bloque jusqu'à ce qu'elle soit terminée.apply_async
envoie une seule tâche à un processus de travail, puis renvoie immédiatement unAsyncResult
objet, qui peut être utilisé pour attendre la fin de la tâche et récupérer le résultat.apply
est implémenté en appelant simplementapply_async(...).get()
Pool
documentation officielle plutôt que dans la documentation ennuyeuse existante .