我正在使用multiprocessing.Pool()
并行化一些繁重的计算。
目标函数返回大量数据(巨大的列表)。我的RAM用完了。
如果没有multiprocessing
,我只需将目标函数更改为生成器,通过yield
一个接一个地生成元素,计算它们。
我理解多处理不支持生成器 - 它等待整个输出并立即返回它,对吧?没有屈服。是否有办法让Pool
工作人员在数据可用时立即生成数据,而无需在RAM中构建整个结果数组?
简单示例:
def target_fnc(arg):
result = []
for i in xrange(1000000):
result.append('dvsdbdfbngd') # <== would like to just use yield!
return result
def process_args(some_args):
pool = Pool(16)
for result in pool.imap_unordered(target_fnc, some_args):
for element in result:
yield element
这是Python 2.7。
答案 0 :(得分:17)
这听起来像是队列的理想用例:http://docs.python.org/2/library/multiprocessing.html#exchanging-objects-between-processes
只需将结果从合并的工作人员提供到队列中,然后在主人中将其摄取。
请注意,除非您将队列耗尽的速度几乎与工作人员填充队列一样快,否则您仍可能会遇到内存压力问题。您可以限制队列大小(适合队列的最大对象数),在这种情况下,池化工作人员将阻塞queue.put
语句,直到队列中有空间可用。这将对内存使用量设置上限。 但如果您这样做,可能是时候重新考虑您是否需要汇集和/或是否可以使用更少的工作人员。
答案 1 :(得分:4)
根据您的描述,听起来您对处理数据的兴趣不大,就像避免传递数百万元素list
一样。
有一种更简单的方法:将数据放入文件中。例如:
def target_fnc(arg):
fd, path = tempfile.mkstemp(text=True)
with os.fdopen(fd) as f:
for i in xrange(1000000):
f.write('dvsdbdfbngd\n')
return path
def process_args(some_args):
pool = Pool(16)
for result in pool.imap_unordered(target_fnc, some_args):
with open(result) as f:
for element in f:
yield element
显然,如果您的结果可能包含换行符,或者不是字符串等,则您需要使用csv
文件,numpy
等代替简单的文本文件,但这个想法是一样的。
话虽如此,即使这更简单,一次处理数据通常有好处,因此分解您的任务或使用Queue
(如其他两个答案所示)可能是更好的是,如果缺点(分别需要一种方法来破坏任务,或者必须能够像生产那样快速地使用数据)不是交易破坏者。
答案 2 :(得分:3)
如果您的任务可以以块的形式返回数据......它们是否可以分解为较小的任务,每个任务都会返回一个块?显然,这并不总是可行的。如果不是,你必须使用其他一些机制(如Queue
,如Loren Abrams建议的那样)。但当它 时,由于其他原因,它可能是一个更好的解决方案,也可以解决这个问题。
以你的例子来说,这肯定是可行的。例如:
def target_fnc(arg, low, high):
result = []
for i in xrange(low, high):
result.append('dvsdbdfbngd') # <== would like to just use yield!
return result
def process_args(some_args):
pool = Pool(16)
pool_args = []
for low in in range(0, 1000000, 10000):
pool_args.extend(args + [low, low+10000] for args in some_args)
for result in pool.imap_unordered(target_fnc, pool_args):
for element in result:
yield element
(如果您愿意,您当然可以用嵌套理解替换循环,或zip
和flatten
替换。)
因此,如果some_args
为[1, 2, 3]
,您将获得300个任务 - [[1, 0, 10000], [2, 0, 10000], [3, 0, 10000], [1, 10000, 20000], …]
,每个任务只返回10000个元素而不是1000000个。