Ответ 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
являются:
- Итерабельность достаточно велика, что преобразование его в список приведет к тому, что вам не хватит/использовать слишком много памяти.
- Вы хотите, чтобы начать обработку результатов, прежде чем все они будут завершены.