当我使用生成器作为multiprocessing.Pool.map函数的可迭代参数时:
pool.map(func, iterable=(x for x in range(10)))
在调用func
之前,似乎发生器已完全耗尽。
我希望产生每个项目并将其传递给每个流程,谢谢
答案 0 :(得分:3)
multiprocessing.map
在处理之前将没有__len__
方法的迭代转换为列表。这样做是为了帮助计算chunksize,池用于对工作程序参数进行分组并降低调度作业的往返成本。这不是最优的,特别是当chunksize为1时,但由于map
必须以某种方式耗尽迭代器,因此它通常不是一个重要的问题。
相关代码位于pool.py
。请注意它使用len
:
def _map_async(self, func, iterable, mapper, chunksize=None, callback=None,
error_callback=None):
'''
Helper function to implement map, starmap and their async counterparts.
'''
if self._state != RUN:
raise ValueError("Pool not running")
if not hasattr(iterable, '__len__'):
iterable = list(iterable)
if chunksize is None:
chunksize, extra = divmod(len(iterable), len(self._pool) * 4)
if extra:
chunksize += 1
if len(iterable) == 0:
chunksize = 0
答案 1 :(得分:1)
import multiprocessing as mp
def e(i):
if i % 1000000 == 0:
print(i)
if __name__ == '__main__':
p = mp.Pool()
def g():
for i in range(100000000):
yield i
print("generator done")
r = p.map(e, g())
p.close()
p.join()
你看到的第一件事是"发电机完成"消息,并且峰值内存使用量过高(正如您怀疑的那样,生成器在任何工作结束之前都会耗尽)。
但是,请更换map()
这样的电话:
r = list(p.imap(e, g()))
现在内存使用仍然很小,并且"生成器已完成"出现在输出端。
然而,你不会等待很长时间才能看到它,因为它非常缓慢:-(imap()
不仅将可迭代的视为是一个可迭代的,但是有效地跨过程边界一次只传递1个项目。为了获得速度,这也有效:
r = list(p.imap(e, g(), chunksize=10000))
在现实生活中,我更有可能迭代imap()
(或imap_unordered()
)结果,而不是强迫它进入列表,然后内存使用仍然很小,无法循环结果也是。
答案 2 :(得分:1)
以 Tim Peters 的回答为基础,这里有一个 jupyter notebook,展示了 imap 和 chunksize 之间的相互作用:
https://gist.github.com/shadiakiki1986/273b3529d3ff7afe2f2cac7b5ac96fe2
它有两个例子:
示例 1 使用 chunksize=1 并具有以下执行:
On CPU 1, execute item 1 from generator
On CPU 2, execute item 2 from generator
When CPU 1 done with item 1, execute item 3 from generator
When CPU 2 done with item 2, execute item 4 from generator
etc
示例 2 的 chunksize=3 执行以下操作
On CPU 1, execute items 1-3 from generator
On CPU 2, execute items 4-6 from generator
When CPU 1 done with items 1-3, execute on 7-9
When CPU 2 done with items 4-6, execute on 10
请注意,在示例 2 中,第 10 项在 CPU 2 上执行,然后在 CPU 1 上执行第 8 项和第 9 项。