multiprocessing.Pool: Jaka jest różnica między map_async a imap?

183

Próbuję nauczyć się korzystać z multiprocessingpakietu Pythona , ale nie rozumiem różnicy między map_asynci imap. Zauważyłem, że zarówno map_asynci imapsą wykonywane asynchronicznie. Więc kiedy powinienem używać jednego na drugim? I w jaki sposób mam odzyskać zwrócony wynik map_async?

Czy powinienem użyć czegoś takiego?

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

result=test()
for i in result:
    print i
spacegoing
źródło

Odpowiedzi:

492

Istnieją dwie kluczowe różnice między imap/ imap_unorderedi map/ map_async:

  1. Sposób, w jaki konsumują iterowalny, który im przekazujesz.
  2. 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:

  1. 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.
  2. Chcesz móc rozpocząć przetwarzanie wyników, zanim wszystkie zostaną zakończone.
dano
źródło
1
co z aplikacjami i aplikacjami?
Harsh Daftary
10
@HarshDaftary applywysyła pojedyncze zadanie do procesu roboczego , a następnie blokuje, dopóki nie zostanie ukończone. apply_asyncwysyła pojedyncze zadanie do procesu roboczego, a następnie natychmiast zwraca AsyncResultobiekt, którego można użyć do oczekiwania na zakończenie zadania i odzyskania wyniku. applyjest realizowane przez zwykłe wywołanieapply_async(...).get()
dano
51
Taki opis powinien znajdować się w oficjalnej Pooldokumentacji, a nie w tej nudnej .
min
@dano Chcę uruchomić funkcję w tle, ale mam pewne ograniczenia zasobów i nie mogę uruchomić tej funkcji tyle razy, ile chcę i chcę ustawić w kolejce dodatkowe wykonanie funkcji. Czy masz pomysł, jak to zrobić? Mam pytanie tutaj . Czy mógłbyś rzucić okiem na moje pytanie i zobaczyć, czy możesz dać mi jakieś wskazówki (a nawet lepiej odpowiedź) na temat tego, jak powinienem to zrobić?
Amir
1
@BallpointBen Przejdzie do następnego dzieła, gdy tylko zostanie wykonane. Zamówienie jest obsługiwane z powrotem w procesie nadrzędnym.
dano