使用大数据进行多处理

时间:2013-02-03 21:02:57

标签: python multiprocessing generator

我正在使用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。

3 个答案:

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

(如果您愿意,您当然可以用嵌套理解替换循环,或zipflatten替换。)

因此,如果some_args[1, 2, 3],您将获得300个任务 - [[1, 0, 10000], [2, 0, 10000], [3, 0, 10000], [1, 10000, 20000], …],每个任务只返回10000个元素而不是1000000个。