imap/imap_unordered 和 map/map_async 之间有两个主要区别:
- 他们使用您传递给他们的迭代的方式。
- 他们将结果返回给您的方式。
map 通过将可迭代对象转换为列表(假设它还不是列表)、将其分成块并将这些块发送到 Pool 中的工作进程来消耗您的可迭代对象。将 iterable 分成块比在进程之间一次传递一个 item 的 iterable 中的每个项目执行得更好 - 特别是在 iterable 很大的情况下。但是,将可迭代对象转换为列表以对其进行分块可能会产生非常高的内存成本,因为整个列表都需要保存在内存中。
imap 不会将您提供的可迭代对象转换为列表,也不会将其分成块(默认情况下)。它将一次迭代一个可迭代的元素,并将它们每个发送到一个工作进程。这意味着您不会将整个可迭代对象转换为列表而对内存造成影响,但这也意味着大型可迭代对象的性能较慢,因为缺少分块。但是,可以通过传递大于默认值 1 的 chunksize 参数来缓解这种情况。
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 的主要原因是:
- 您的可迭代对象足够大,将其转换为列表会导致您耗尽/使用太多内存。
- 您希望能够在所有完成之前开始处理结果。