如何在python中并行化大的for循环

时间:2013-08-29 19:23:37

标签: python list parallel-processing multiprocessing

我刚接触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。那是对的吗?

因此,这种并行化对我不起作用,因为我不需要将所有参数存储在列表中。有一个更好的方法吗?我感谢任何建议或参考。谢谢。

1 个答案:

答案 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)