我刚接触Python,我仍处于学习曲线的陡峭阶段。感谢您提前发表任何意见。
我有一个很大的for循环运行(在许多迭代意义上很大),例如:
for i in range(10000)
for j in range(10000)
f((i,j))
我认为这是一个常见的问题,如何将其并行化,并且在谷歌搜索了几个小时之后,我使用“多处理”模块到达了解决方案,如下所示:
pool=Pool()
x=pool.map(f,[(i,j) for i in range(10000) for j in range(10000)])
这在循环很小时有效。但是,如果循环很大,它实际上很慢,或者如果循环太大,有时会发生内存错误。似乎python首先生成参数列表,然后将列表提供给函数“f”,甚至使用xrange。那是对的吗?
因此,这种并行化对我不起作用,因为我不需要将所有参数存储在列表中。有一个更好的方法吗?我感谢任何建议或参考。谢谢。
答案 0 :(得分:4)
似乎python会首先生成参数列表,然后将列表提供给函数“f”,甚至使用xrange。这是对的吗?
是的,因为你正在使用列表推导,它明确要求它生成该列表。
(注意xrange
在这里并不真正相关,因为你一次只有两个范围,每个长10K;与参数列表的100M相比,这没什么。)
如果您希望它根据需要动态生成值,而不是一次生成所有100M,您希望使用生成器表达式而不是列表推导。这几乎总是将括号括在括号中:
x=pool.map(f,((i,j) for i in range(10000) for j in range(10000)))
但是,正如您可以从the source看到的那样,map
最终只会列出一个列表,如果您给它一个生成器,那么在这种情况下,这将无法解决任何问题。 (文档没有明确说明这一点,但很难看出它如何能够选择一个好的chunksize来切断迭代,如果它没有长度......)。
而且,即使这不是真的,你仍然会在结果中再次遇到同样的问题,因为pool.map
会返回一个列表。
要解决这两个问题,您可以改用pool.imap
。它懒惰地使用迭代,并返回结果的惰性迭代器。
有一点需要注意的是imap
如果你没有传递一个,那就不会猜到最好的chunksize,但是默认为1
,所以你可能需要一些思考或试验&错误以优化它。
此外,imap
在进入时仍会排队一些结果,因此它可以按照与参数相同的顺序将它们反馈给您。在病理情况下,它可能最终排队(poolize-1)/ poolize你的结果,虽然在实践中这是非常罕见的。如果要解决此问题,请使用imap_unordered
。如果您需要知道排序,只需使用args和结果来回传递索引:
args = ((i, j) for i in range(10000) for j in range(10000))
def indexed_f(index, (i, j)):
return index, f(i, j)
results = pool.imap_unordered(indexed_f, enumerate(args))
但是,我注意到在您的原始代码中,您根本没有对f(i, j)
的结果做任何事情。在那种情况下,为什么甚至懒得收集结果呢?在这种情况下,您可以回到循环:
for i in range(10000):
for j in range(10000):
map.apply_async(f, (i,j))
但是,imap_unordered
可能仍然值得使用,因为它提供了一种非常简单的方法来阻止所有任务完成,同时仍然让池本身运行以供以后使用:
def consume(iterator):
deque(iterator, max_len=0)
x=pool.imap_unordered(f,((i,j) for i in range(10000) for j in range(10000)))
consume(x)