multiprocessing.Pool: qual è la differenza tra map_async e imap?


184

Sto cercando di imparare come usare il multiprocessingpacchetto di Python , ma non capisco la differenza tra map_asynce imap. Ho notato che entrambi map_asynce imapvengono eseguiti in modo asincrono. Quindi, quando dovrei usarne uno sopra l'altro? E come devo recuperare il risultato restituito da map_async?

Dovrei usare qualcosa del genere?

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

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

Risposte:


492

Esistono due differenze chiave tra imap/ imap_unorderede map/ map_async:

  1. Il modo in cui consumano l'iterabile che passi a loro.
  2. Il modo in cui ti restituiscono il risultato.

mapconsuma il tuo iterabile convertendo l'iterabile in un elenco (supponendo che non sia già un elenco), suddividendolo in blocchi e inviando quei blocchi ai processi di lavoro in Pool. La suddivisione dell'iterabile in blocchi ha prestazioni migliori rispetto al passaggio di ciascun elemento nell'iterabile tra i processi un elemento alla volta, in particolare se l'iterabile è grande. Tuttavia, trasformando l'iterabile in un elenco per ridurlo, può avere un costo di memoria molto elevato, poiché l'intero elenco dovrà essere tenuto in memoria.

imapnon trasforma l'iterabile che lo dai in un elenco, né lo divide in blocchi (per impostazione predefinita). Esaminerà l'iterabile un elemento alla volta e li invierà a un processo di lavoro. Ciò significa che non si prende il colpo di memoria del convertire l'intero iterabile in un elenco, ma significa anche che le prestazioni sono più lente per i grandi iterabili, a causa della mancanza di blocchi. Questo può essere mitigato passando un chunksizeargomento più grande del valore predefinito di 1, tuttavia.

L'altra grande differenza tra imap/ imap_unorderede map/ map_async, è che con imap/ imap_unordered, puoi iniziare a ricevere risultati dai lavoratori non appena sono pronti, piuttosto che dover aspettare che finiscano tutti. Con map_async, AsyncResultviene immediatamente restituito un, ma non è possibile effettivamente recuperare i risultati da quell'oggetto fino a quando non sono stati elaborati tutti, a quel punto restituisce lo stesso elenco che mapviene ( mapeffettivamente implementato internamente come map_async(...).get()). Non c'è modo di ottenere risultati parziali; o hai l'intero risultato o niente.

imaped imap_unorderedentrambi restituiscono subito iterabili. Con imap, i risultati saranno ottenuti dall'iterabile non appena saranno pronti, pur mantenendo l'ordinamento dell'input iterabile. Con imap_unordered, i risultati saranno prodotti non appena saranno pronti, indipendentemente dall'ordine dell'input iterabile. Quindi, supponiamo di avere questo:

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)))

Questo produrrà:

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

Se usi p.imap_unorderedinvece di p.imap, vedrai:

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

Se usi p.mapo p.map_async().get(), vedrai:

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

Quindi, i motivi principali per utilizzare imap/ imap_unorderedsopra map_asyncsono:

  1. Il tuo iterabile è abbastanza grande che la conversione in un elenco potrebbe farti esaurire / utilizzare troppa memoria.
  2. Si vuole essere in grado di avviare l'elaborazione i risultati prima di tutto di loro sono stati completati.

1
che dire di apply e apply_async?
Harsh Daftary,

10
@HarshDaftary applyinvia una singola attività a un processo di lavoro, quindi si blocca fino al completamento. apply_asyncinvia una singola attività a un processo di lavoro, quindi restituisce immediatamente un AsyncResultoggetto, che può essere utilizzato per attendere il completamento dell'attività e recuperare il risultato. applyè implementato semplicemente chiamandoapply_async(...).get()
dano il

51
Questo è il tipo di descrizione che dovrebbe essere nella Pooldocumentazione ufficiale piuttosto che quella noiosa esistente .
Min

@dano Voglio eseguire una funzione in background ma ho alcune limitazioni di risorse e non posso eseguire la funzione tutte le volte che voglio e voglio mettere in coda le esecuzioni extra della funzione. Hai idea di come dovrei farlo? Ho la mia domanda qui . Potresti per favore dare un'occhiata alla mia domanda e vedere se puoi darmi qualche suggerimento (o meglio, una risposta) su come dovrei farlo?
Amir

1
@BallpointBen Passerà al prossimo lavoro non appena avrà finito. L'ordinamento viene gestito nel processo principale.
dano,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.