【问题标题】:how to parallelize big for loops in python如何在python中并行化大for循环
【发布时间】:2013-09-02 09:17:30
【问题描述】:

我刚接触 Python,我仍处于学习曲线的陡峭阶段。谢谢你前面的任何cmets。

我有一个很大的 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。那是对的吗?

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

【问题讨论】:

  • 简而言之:这没有任何意义,因为 Python 使用了全局解释器锁,它可以防止多个线程同时运行。如果您使用 Jython 或 IronPython,您可能仍然有一些好处。 wiki.python.org/moin/GlobalInterpreterLock
  • @GiulioFranco 虽然他使用的是multiprocessing,而不是threading,所以GIL 没有发挥作用。

标签: python list parallel-processing multiprocessing


【解决方案1】:

似乎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 如果您给它一个生成器,最终只会列出一个列表,所以在这种情况下,这不会解决任何问题。 (文档没有明确说明这一点,但是如果它没有长度,很难看出它如何选择一个好的块大小来将可迭代对象切碎……)。

而且,即使这不是真的,您仍然会在结果中再次遇到同样的问题,因为pool.map 返回一个列表。

要解决这两个问题,您可以改用pool.imap。它惰性地消耗可迭代对象,并返回结果的惰性迭代器。

需要注意的一点是,imap 不会猜测最佳块大小,如果你没有通过一个,而是默认为 1,所以你可能需要一些思考或反复试验来优化它。

此外,imap 仍会在某些结果进入时将它们排队,因此它可以按照与参数相同的顺序将它们反馈给您。在病态的情况下,它最终可能会排队 (poolsize-1)/poolsize 的结果,尽管在实践中这种情况非常罕见。如果你想解决这个问题,请使用imap_unordered。如果您需要知道顺序,只需将索引与参数和结果一起来回传递:

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)

【讨论】:

  • 我仍然会使用xrange,只是为了保持这里的一致性,并且如果range(n) 中的n 稍后会增长。
  • @Anorov:与什么的一致性?我可能也会使用它……但它不会有太大的不同。 (如果 n 增长,n**2 会增长得更快。)
  • @abarnet 一致性在于整个表达式是惰性的。你说得对,这没什么区别,但我认为养成在 Python 2.x 中使用 xrange 的习惯是件好事。
  • @Anorov:同意这是一个好习惯,这正是我使用它的原因。我只是在解释为什么它实际上与 OP 试图解决的问题无关。
  • @Anorov,谢谢!我尝试了生成器而不是列表理解。似乎 python 仍然会首先产生一系列参数并将其提供给函数。所以我认为 imap 是要走的路。另外,我很抱歉在帖子中没有明确说明我希望得到 f(i,j) 的结果,因此 imap_async 和 apply_sync 都可以使用您将索引作为参数传递的技巧。再次感谢您的帮助。
猜你喜欢
  • 2017-03-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多