Есть два ключевых различия между 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
:
- Ваша итерация достаточно велика, поэтому ее преобразование в список может привести к нехватке / использованию слишком большого количества памяти.
- Вы хотите иметь возможность начать обработку результатов до того, как все они будут завершены.
apply
отправляет одну задачу рабочему процессу, а затем блокирует ее до завершения.apply_async
отправляет одну задачу в рабочий процесс, а затем немедленно возвращаетAsyncResult
объект, который можно использовать для ожидания завершения задачи и получения результата.apply
реализуется простым вызовомapply_async(...).get()
Pool
документации, а не в существующей скучной .