multiprocessing.Pool: В чем разница между map_async и imap?

184

Я пытаюсь научиться использовать multiprocessingпакет Python , но я не понимаю разницу между map_asyncи imap. Я заметил, что оба map_asyncи imapвыполняются асинхронно. Так, когда я должен использовать один по другому? И как я должен получить результат, возвращенный map_async?

Должен ли я использовать что-то вроде этого?

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

result=test()
for i in result:
    print i
spacegoing
источник

Ответы:

492

Есть два ключевых различия между imap/ imap_unorderedи map/ map_async:

  1. То, как они потребляют итеративное, вы передаете им.
  2. То, как они возвращают результат обратно к вам.

mapпотребляет вашу итерируемую, преобразовывая итерируемую в список (предполагая, что это уже не список), разбивая его на порции и отправляя эти порции рабочим процессам в Pool. Разбиение итерируемого на куски работает лучше, чем передача каждого элемента в итерируемом между процессами по одному элементу за раз - особенно если итерация велика. Однако, превращение итерируемого в список для того, чтобы разделить его на части, может иметь очень высокую стоимость памяти, так как весь список должен храниться в памяти.

imapне превращает итерируемое вами в список и не разбивает его на куски (по умолчанию). Он будет перебирать итерируемый по одному элементу за раз и отправлять каждый из них в рабочий процесс. Это означает, что вы не берете на себя удар памяти при преобразовании всего итерируемого в список, но это также означает, что производительность для больших итераций ниже из-за отсутствия разбиения на фрагменты. Однако это можно уменьшить, передав chunksizeаргумент, больший, чем значение по умолчанию, равное 1.

Другое существенное отличие между imap/ imap_unorderedи map/ map_asyncсостоит в том, что с imap/ imap_unorderedвы можете начать получать результаты от работников, как только они будут готовы, вместо того, чтобы ждать, пока все они будут закончены. С помощью map_async, AsyncResultвозвращается сразу, но вы не можете на самом деле получить результаты из этого объекта, пока все они не будут обработаны, и в этот момент он возвращает тот же список, что mapи ( mapфактически реализуется как map_async(...).get()). Там нет никакого способа получить частичные результаты; у вас либо есть весь результат, либо ничего.

imapи imap_unorderedоба сразу возвращают итерации. При imapэтом результаты будут получены из итерируемого, как только они будут готовы, при этом сохраняя порядок ввода итерируемого. При этом imap_unorderedрезультаты будут получены, как только они будут готовы, независимо от порядка повторяемого ввода. Итак, скажем, у вас есть это:

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

Это выведет:

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

Если вы используете p.imap_unorderedвместо p.imap, вы увидите:

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

Если вы используете p.mapили p.map_async().get(), вы увидите:

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

Итак, основные причины для использования imap/ imap_unorderedболее map_async:

  1. Ваша итерация достаточно велика, поэтому ее преобразование в список может привести к нехватке / использованию слишком большого количества памяти.
  2. Вы хотите иметь возможность начать обработку результатов до того, как все они будут завершены.
Дано
источник
1
как насчет apply и apply_async?
Суровый Дафтары
10
@HarshDaftary applyотправляет одну задачу рабочему процессу, а затем блокирует ее до завершения. apply_asyncотправляет одну задачу в рабочий процесс, а затем немедленно возвращает AsyncResultобъект, который можно использовать для ожидания завершения задачи и получения результата. applyреализуется простым вызовомapply_async(...).get()
дано
51
Это описание, которое должно быть в официальной Poolдокументации, а не в существующей скучной .
мин
@dano Я хочу запустить функцию в фоновом режиме, но у меня есть некоторые ограничения по ресурсам, и я не могу запускать функцию столько раз, сколько я хочу и хочу поставить в очередь дополнительные исполнения функции. У тебя есть идеи, как мне это сделать? У меня есть мой вопрос здесь . Не могли бы вы взглянуть на мой вопрос и посмотреть, можете ли вы дать мне несколько советов (или, что еще лучше, ответ) о том, как мне это сделать?
Амир
1
@BallpointBen Он перейдет к следующей части работы, как только это будет сделано. Заказ обрабатывается обратно в родительском процессе.
Дано