Istnieją dwie kluczowe różnice między imap/ imap_unorderedi map/ map_async:
- Sposób, w jaki konsumują iterowalny, który im przekazujesz.
- Sposób, w jaki zwracają wynik.
mapzużywa twoją iterowalność, konwertując iterowalną na listę (zakładając, że nie jest to już lista), dzieląc ją na części i wysyłając te części do procesów roboczych w Pool. Podział iteracji na części jest skuteczniejszy niż przekazywanie każdego elementu w iteracji między procesami jeden element na raz - szczególnie jeśli iteracja jest duża. Jednak przekształcenie iterowalnego w listę w celu podzielenia na fragmenty może mieć bardzo wysoki koszt pamięci, ponieważ cała lista będzie musiała być przechowywana w pamięci.
imapnie zamienia iterowalnego kodu, który dajesz mu na listę, ani nie dzieli go na części (domyślnie). Będzie iterował po iterowalnym jednym elemencie na raz i wysyłał je do procesu roboczego. Oznacza to, że nie bierzesz pod uwagę pamięci przy konwertowaniu całej iterowalności na listę, ale oznacza to również, że wydajność jest mniejsza w przypadku dużych iteracji z powodu braku porcji. Można to jednak złagodzić, przekazując chunksizeargument większy niż domyślny 1.
Inną ważną różnicą między imap/ imap_unorderedi map/ map_asyncjest to, że za pomocą imap/ imap_unorderedmożesz zacząć otrzymywać wyniki od pracowników, gdy tylko będą gotowi, zamiast czekać na ich ukończenie. Za pomocą map_async, AsyncResultzwracane jest od razu, ale nie można faktycznie pobrać wyników z tego obiektu, dopóki wszystkie nie zostaną przetworzone, w którym to momencie zwraca tę samą listę, co maprobi ( mapjest faktycznie zaimplementowana wewnętrznie jako map_async(...).get()). Nie ma sposobu, aby uzyskać częściowe wyniki; albo masz cały wynik, albo nic.
imapi imap_unorderedoba zwracają iterable od razu. Dzięki imap, wyniki zostaną wygenerowane z iterowalnego, gdy tylko będą gotowe, przy jednoczesnym zachowaniu kolejności iterowalnej wejściowej. Dzięki imap_unorderedwyniki zostaną wyświetlone, gdy tylko będą gotowe, niezależnie od kolejności iteracji danych wejściowych. Powiedzmy, że masz:
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)))
Spowoduje to:
3 (Time elapsed: 1s)
7 (Time elapsed: 5s)
5 (Time elapsed: 5s)
Jeśli użyjesz p.imap_unorderedzamiast p.imap, zobaczysz:
3 (Time elapsed: 1s)
5 (Time elapsed: 3s)
7 (Time elapsed: 5s)
Jeśli użyjesz p.maplub p.map_async().get(), zobaczysz:
3 (Time elapsed: 5s)
7 (Time elapsed: 5s)
5 (Time elapsed: 5s)
Tak więc główne powody używania imap/ imap_unorderedprzerzucenia map_asyncto:
- Twoja iterowalność jest na tyle duża, że przekonwertowanie jej na listę spowoduje, że zabraknie Ci pamięci / zużyjesz za dużo pamięci.
- Chcesz móc rozpocząć przetwarzanie wyników, zanim wszystkie zostaną zakończone.