web-dev-qa-db-fra.com

multiprocessing.Pool: Quelle est la différence entre map_async et imap?

J'essaie d'apprendre à utiliser le paquetage multiprocessing de Python, mais je ne comprends pas la différence entre map_async et imap. J'ai remarqué que map_async et imap sont exécutés de manière asynchrone. Alors, quand devrais-je utiliser l'un sur l'autre? Et comment dois-je récupérer le résultat renvoyé par map_async?

Devrais-je utiliser quelque chose comme ça?

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

result=test()
for i in result:
    print i
153
spacegoing

Il existe deux différences principales entre imap/imap_unordered et map/map_async:

  1. La façon dont ils consomment l'itérable que vous leur passez.
  2. La façon dont ils vous renvoient le résultat.

map utilise votre itérable en convertissant l'itérable en liste (en supposant qu'il ne soit pas déjà une liste), en la divisant en morceaux et en envoyant ces morceaux aux processus de travail dans le Pool. Décomposer les éléments itérables en morceaux est plus efficace que de passer chaque élément de cet élément à un autre, en particulier si cet élément est volumineux. Cependant, transformer l'itérable en liste afin de le réduire en bloc peut avoir un coût en mémoire très élevé, car toute la liste devra être conservée en mémoire.

imap ne transforme pas l'itéré que vous lui donnez en liste, ni ne le divise en morceaux (par défaut). Il itérera sur un élément itérable à la fois et les enverra chacun à un processus de travail. Cela signifie que vous ne prenez pas la mémoire de la conversion de l'ensemble itérable en une liste, mais cela signifie également que la performance est plus lente pour les grands itérables, en raison du manque de fragmentation. Cela peut toutefois être atténué en passant un argument chunksize supérieur à 1 par défaut, égal à 1.

L’autre grande différence entre imap/imap_unordered et map/map_async, c’est que, avec imap/imap_unordered, vous pouvez commencer à recevoir les résultats des travailleurs dès qu’ils sont prêts. attendre que tout soit fini. Avec map_async, un AsyncResult est renvoyé immédiatement, mais vous ne pouvez pas extraire les résultats de cet objet tant que tous n'ont pas été traités. La liste est alors renvoyée à la même liste que map (map est effectivement appliqué à l'interne comme map_async(...).get()). Il n'y a aucun moyen d'obtenir des résultats partiels; soit vous avez le résultat entier, soit rien.

imap et imap_unordered renvoient tous les deux des iterables. Avec imap, les résultats seront générés à partir de l'itéré dès qu'ils seront prêts, tout en préservant l'ordre des entrées. Avec imap_unordered, les résultats seront fournis dès qu’ils seront prêts, quel que soit l’ordre de l’entrée itérable. Alors, dites que vous avez ceci:

import multiprocessing
import time

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

if __== "__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 p.imap_unordered au lieu 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 sur map_async sont les suivantes:

  1. Votre nombre itérable est suffisamment grand pour que le convertir en liste vous empêche d'utiliser/d'utiliser trop de mémoire.
  2. Vous voulez pouvoir commencer à traiter les résultats avant que tous d'entre eux soient terminés.
405
dano