Multiprocessing.Pool: Какая разница между map_async и imap?

Я пытаюсь узнать, как использовать пакет Python multiprocessing, но я не понимаю разницы между 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

Ответ 1

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

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

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 являются:

  • Итерабельность достаточно велика, что преобразование его в список приведет к тому, что вам не хватит/использовать слишком много памяти.
  • Вы хотите, чтобы начать обработку результатов, прежде чем все они будут завершены.