multiprocessing.Pool: Qual é a diferença entre map_async e imap?

184

Estou tentando aprender a usar o multiprocessingpacote do Python , mas não entendo a diferença entre map_asynce imap. Notei que ambos map_asynce imapsão executados de forma assíncrona. Então, quando devo usar um sobre o outro? E como devo recuperar o resultado retornado por map_async?

Devo usar algo assim?

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

result=test()
for i in result:
    print i
viagem espacial
fonte

Respostas:

492

Existem duas diferenças principais entre imap/ imap_unorderede map/ map_async:

  1. A maneira como eles consomem o iterável que você passa para eles.
  2. A maneira como eles retornam o resultado para você.

map consome seu iterável convertendo o iterável em uma lista (supondo que não seja uma lista já), dividindo-o em pedaços e enviando-os para os processos de trabalho no diretório Pool . Quebrar o iterável em partes tem um desempenho melhor do que passar cada item no iterável entre processos, um item de cada vez - principalmente se o iterável for grande. No entanto, transformar o iterável em uma lista para fragmentá-lo pode ter um custo de memória muito alto, pois a lista inteira precisará ser mantida na memória.

imapnão transforma o iterável que você colocou em uma lista, nem o divide em partes (por padrão). Ele iterará sobre o elemento iterável por vez e os enviará para um processo de trabalho. Isso significa que você não leva o hit de memória de converter o iterável inteiro para uma lista, mas também significa que o desempenho é mais lento para iterables grandes, devido à falta de chunking. Isso pode ser atenuado passando um chunksizeargumento maior que o padrão de 1, no entanto.

A outra grande diferença entre imap/ imap_unorderede map/ map_asyncé que, com imap/ imap_unordered, você pode começar a receber resultados dos trabalhadores assim que eles estiverem prontos, em vez de esperar que todos eles sejam concluídos. Com map_async, um AsyncResulté retornado imediatamente, mas você não pode recuperar os resultados desse objeto até que todos eles tenham sido processados; nesse momento, ele retorna a mesma lista que mapfaz ( mapé realmente implementado internamente como map_async(...).get()). Não há como obter resultados parciais; você tem o resultado inteiro ou nada.

imape imap_unorderedambos retornam iterables imediatamente. Com imap, os resultados serão gerados a partir do iterável assim que estiverem prontos, preservando ainda a ordenação da entrada iterável. Com imap_unordered, os resultados serão gerados assim que estiverem prontos, independentemente da ordem da entrada iterável. Então, diga que você tem isso:

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

Isso produzirá:

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

Se você usar em p.imap_unorderedvez de p.imap, verá:

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

Se você usar p.mapou p.map_async().get(), verá:

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

Portanto, os principais motivos para usar imap/ imap_unorderedover map_asyncsão:

  1. Seu iterável é grande o suficiente para convertê-lo em uma lista, causando a falta / uso de muita memória.
  2. Você deseja iniciar o processamento dos resultados antes que todos eles sejam concluídos.
dano
fonte
1
que tal aplicar e apply_async?
Harsh Daftary
10
O @HarshDaftary applyenvia uma única tarefa para um processo de trabalho e depois bloqueia até que seja concluída. apply_asyncenvia uma única tarefa para um processo de trabalho e retorna imediatamente um AsyncResultobjeto, que pode ser usado para aguardar a conclusão da tarefa e recuperar o resultado. applyé implementado simplesmente chamandoapply_async(...).get()
dano
51
Esse é o tipo de descrição que deve estar na Pooldocumentação oficial , e não na documentação monótona existente .
mins
@dano Quero executar uma função em segundo plano, mas tenho algumas limitações de recursos e não posso executar a função quantas vezes quiser e desejar enfileirar as execuções extras da função. Você tem alguma idéia de como eu devo fazer isso? Eu tenho minha pergunta aqui . Poderia, por favor, dar uma olhada na minha pergunta e ver se você pode me dar algumas dicas (ou melhor ainda, uma resposta) sobre como devo fazer isso?
Amir
1
@BallpointBen Passará para o próximo trabalho assim que terminar. A encomenda é processada novamente no processo pai.
dano 28/08