multiprocessing.Pool: Quando usar apply, apply_async ou map?

Respostas:

424

Nos velhos tempos do Python, para chamar uma função com argumentos arbitrários, você usaria apply:

apply(f,args,kwargs)

applyainda existe no Python2.7, embora não no Python3, e geralmente não é mais usado. Hoje em dia,

f(*args,**kwargs)

é preferível. Os multiprocessing.Poolmódulos tentam fornecer uma interface semelhante.

Pool.applyé como Python apply, exceto que a chamada da função é realizada em um processo separado. Pool.applybloqueia até que a função seja concluída.

Pool.apply_asynctambém é como o built-in do Python apply, exceto que a chamada retorna imediatamente em vez de esperar pelo resultado. Um AsyncResultobjeto é retornado. Você chama seu get()método para recuperar o resultado da chamada de função. O get()método bloqueia até que a função seja concluída. Assim, pool.apply(func, args, kwargs)é equivalente a pool.apply_async(func, args, kwargs).get().

Por outro lado Pool.apply, o Pool.apply_asyncmétodo também possui um retorno de chamada que, se fornecido, é chamado quando a função é concluída. Isso pode ser usado em vez de chamar get().

Por exemplo:

import multiprocessing as mp
import time

def foo_pool(x):
    time.sleep(2)
    return x*x

result_list = []
def log_result(result):
    # This is called whenever foo_pool(i) returns a result.
    # result_list is modified only by the main process, not the pool workers.
    result_list.append(result)

def apply_async_with_callback():
    pool = mp.Pool()
    for i in range(10):
        pool.apply_async(foo_pool, args = (i, ), callback = log_result)
    pool.close()
    pool.join()
    print(result_list)

if __name__ == '__main__':
    apply_async_with_callback()

pode produzir um resultado como

[1, 0, 4, 9, 25, 16, 49, 36, 81, 64]

Observe que, diferentemente pool.map, a ordem dos resultados pode não corresponder à ordem em que as pool.apply_asyncchamadas foram feitas.


Portanto, se você precisar executar uma função em um processo separado, mas desejar que o processo atual seja bloqueado até que a função retorne, use Pool.apply. Como Pool.apply, Pool.mapbloqueia até que o resultado completo seja retornado.

Se você deseja que o conjunto de processos do operador execute várias chamadas de função de forma assíncrona, use Pool.apply_async. A ordem dos resultados não é garantida como a ordem das chamadas para Pool.apply_async.

Observe também que você pode chamar várias funções diferentesPool.apply_async (nem todas as chamadas precisam usar a mesma função).

Por outro lado, Pool.mapaplica a mesma função a muitos argumentos. No entanto, diferentemente Pool.apply_async, os resultados são retornados em uma ordem correspondente à ordem dos argumentos.

unutbu
fonte
11
Deveria haver if __name__=="__main__"antes apply_async_with_callback()no Windows?
JFS
3
Muito obrigado. que tal map_async?
Phyo Arkar Lwin
38
Olhe dentro multiprocessing / pool.py e você verá que isso Pool.map(func,iterable)é equivalente a Pool.map_async(func,iterable).get(). Portanto, a relação entre Pool.mape Pool.map_asyncé semelhante à de Pool.applye Pool.apply_async. Os asynccomandos retornam imediatamente, enquanto os não- asynccomandos bloqueiam. Os asynccomandos também têm um retorno de chamada.
unutbu
7
Decidir entre usar Pool.mape Pool.applyé semelhante a decidir quando usar mapou applyno Python. Você acabou de usar a ferramenta adequada ao trabalho. A decisão entre usar a versão asynce a não- asyncversão depende se você deseja que a chamada bloqueie o processo atual e / ou se você deseja usar o retorno de chamada.
unutbu
6
@falsePockets: Sim. Cada chamada para apply_asyncretorna um ApplyResultobjeto. Chamando que ApplyResult's getmétodo irá retornar o valor de retorno da função associada (ou raise mp.TimeoutErrorse o tempo limite. Chamada) Então, se você colocar os ApplyResults em uma lista ordenada, em seguida, chamando seus getmétodos irá retornar os resultados na mesma ordem. Você poderia apenas usar pool.mapnesta situação no entanto.
Unutbu
75

Em relação a applyvs map:

pool.apply(f, args): fé executado apenas em UM dos trabalhadores da piscina. Portanto, um dos processos no pool será executado f(args).

pool.map(f, iterable): Esse método divide o iterável em vários pedaços que ele envia ao pool de processos como tarefas separadas. Então você tira proveito de todos os processos no pool.

kakhkAtion
fonte
4
e se o iterable é um gerador
RustyShackleford
Hmm ... boa pergunta. Para ser honesto eu não já piscinas com geradores usados, mas esta discussão pode ser útil: stackoverflow.com/questions/5318936/...
kakhkAtion
@kakhkAtion Em relação à aplicação, se apenas um dos trabalhadores executa a função, o que o restante dos trabalhadores faz? Preciso chamar Apply várias vezes para que o restante dos trabalhadores execute uma tarefa?
Moondra
3
Verdade. Também dê uma olhada em pool.apply_async se você quiser almoçar trabalhadores de forma assíncrona. "pool_apply bloqueia até que o resultado esteja pronto, portanto, apply_async () é mais adequado para a execução de trabalhos em paralelo"
kakhkAtion
1
O que acontece quando eu tenho 4 processos, mas liguei apply_async()8 vezes? Ele irá lidar automaticamente com uma fila?
Saravanabalagi Ramachandran
31

Aqui está uma visão geral em formato de tabela, a fim de mostrar as diferenças entre Pool.apply, Pool.apply_async, Pool.mape Pool.map_async. Ao escolher um, você deve levar em consideração vários argumentos, simultaneidade, bloqueio e pedido:

                  | Multi-args   Concurrence    Blocking     Ordered-results
---------------------------------------------------------------------
Pool.map          | no           yes            yes          yes
Pool.map_async    | no           yes            no           yes
Pool.apply        | yes          no             yes          no
Pool.apply_async  | yes          yes            no           no
Pool.starmap      | yes          yes            yes          yes
Pool.starmap_async| yes          yes            no           no

Notas:

  • Pool.imape Pool.imap_async- versão mais lenta do mapa e map_async.

  • Pool.starmap método, muito semelhante ao método map, além da aceitação de vários argumentos.

  • AsyncOs métodos enviam todos os processos de uma só vez e recuperam os resultados assim que terminam. Use o método get para obter os resultados.

  • Pool.map(ou Pool.apply) métodos são muito semelhantes ao mapa interno do Python (ou aplicam-se). Eles bloqueiam o processo principal até que todos os processos sejam concluídos e retornem o resultado.

Exemplos:

mapa

É chamado para uma lista de trabalhos de uma só vez

results = pool.map(func, [1, 2, 3])

Aplique

Só pode ser chamado para um emprego

for x, y in [[1, 1], [2, 2]]:
    results.append(pool.apply(func, (x, y)))

def collect_result(result):
    results.append(result)

map_async

É chamado para uma lista de trabalhos de uma só vez

pool.map_async(func, jobs, callback=collect_result)

apply_async

Só pode ser chamado para um trabalho e executa um trabalho em segundo plano em paralelo

for x, y in [[1, 1], [2, 2]]:
    pool.apply_async(worker, (x, y), callback=collect_result)

mapa das estrelas

É uma variante da pool.mapqual suporta vários argumentos

pool.starmap(func, [(1, 1), (2, 1), (3, 1)])

starmap_async

Uma combinação de starmap () e map_async () que itera sobre iterável de iterables e chama func com os iterables descompactados. Retorna um objeto de resultado.

pool.starmap_async(calculate_worker, [(1, 1), (2, 1), (3, 1)], callback=collect_result)

Referência:

Encontre a documentação completa aqui: https://docs.python.org/3/library/multiprocessing.html

Rene B.
fonte
2
Pool.starmap () está bloqueando
Alan Evangelista