对多处理工作器输出数组求和,而不是返回整个结果列表

时间:2015-11-25 12:20:16

标签: python multiprocessing

我理解如何使用Python的multiprocessing模块进行并行处理,方法是将作业分成块,将其传递给工作池,然后编译结果列表。如何处理工作池中的输出,而不是首先获取整个结果列表然后完全处理它?<​​/ p>

当感兴趣的输出是大量结果的总和(或类似的聚合函数)时,这很重要。将所有结果存储在内存中是缓慢且不必要的,如果系统内存不足以存储所有结果,则甚至可能无法存储。

我知道如何使用,例如an incrementing counter。但在我的情况下,输出是数组,我不知道如何最好地在运行中总结它们。

最小的工作示例

现实生活中的用例是核密度估计,但我已经将它提炼到这个例子。我有一个类,其方法将数组作为输入并返回相同大小的数组。我出于显而易见的原因,一直在使用numpy。

import numpy as np

class MyClass(object):
    def __init__(self, val):
        self.val = val

    def generate_array(self, input_arr):
        return np.exp(-(input_arr - self.val) ** 2)

我从这个类中创建了一个大的对象列表,然后依次将相同的输入数组传递给每个<{1}}方法。我最终想要这些数组的总和。

以下是三种直接使用标准串行循环方法计算的方法,generate_arraymap具有不同的块大小。

imap

在我的8核机器上输出

import numpy as np
import operator
from functools import partial
from time import time
import multiprocessing as mp   

def serial_evaluation(obj_arr, input_arr):
    return reduce(operator.add, (t.generate_array(input_arr) for t in obj_arr))


def parallel_func(obj, input_arr=None):
    return obj.generate_array(input_arr)


def parallel_evaluation(obj_arr, input_arr):
    pool = mp.Pool()
    the_func = partial(parallel_func, input_arr=input_arr)
    z = pool.map(the_func, obj_arr)
    return np.array(z).sum(axis=0)


def parallel_evaluation_imap(obj_arr, input_arr, chunksize=1):
    pool = mp.Pool()
    z = np.zeros_like(input_arr)
    the_func = partial(parallel_func, input_arr=input_arr)
    jobs = pool.imap(the_func, obj_arr, chunksize=chunksize)
    for x in jobs:
        z += x
    return z

if __name__ == "__main__":
    n = 20000

    # create array of objects of type MyClass
    obj_arr = [MyClass(t) for t in np.random.rand(n)]
    # define input array
    input_arr = np.linspace(-2, 3, n)

    tic = time()
    res_serial = serial_evaluation(obj_arr, input_arr)
    print "Serial evaluation in %f s" % (time()- tic)

    tic = time()
    res_parallel = parallel_evaluation(obj_arr, input_arr)
    print "Parallel evaluation in %f s" % (time()- tic)

    # different imap chunksizes
    chunksizes = [1, 4, 16, 64, 256, 1024, 2048, 8192]
    res_parallel_imap = {}
    for c in chunksizes:
        tic = time()
        res_parallel_imap[c] = parallel_evaluation_imap(obj_arr, input_arr, chunksize=c)
        print "Parallel imap evaluation (chunksize %d) in %f s" % (c, time()- tic)

我预计速度提升不会超过8倍,但是不到2意味着对我来说显着的多处理开销。 Serial evaluation in 7.657961 s Parallel evaluation in 4.888544 s Parallel imap evaluation (chunksize 1) in 11.662317 s Parallel imap evaluation (chunksize 4) in 5.779365 s Parallel imap evaluation (chunksize 16) in 5.929825 s Parallel imap evaluation (chunksize 64) in 4.487672 s Parallel imap evaluation (chunksize 256) in 4.114722 s Parallel imap evaluation (chunksize 1024) in 4.600147 s Parallel imap evaluation (chunksize 2048) in 5.048849 s Parallel imap evaluation (chunksize 8192) in 7.285429 s 版本也会通过我的系统RAM进行咀嚼,因为它必须在聚合之前生成20000 x 20000阵列。这不符合我的实际用例要求。 map版本没有相同的RAM问题(最高块大小除外),并且是当前的前沿版本。

我还能怎样处理这个问题?我考虑过的想法:

  • 工作人员以不同的方式汇总结果?我觉得this article on PyMOTW可能会被改编,但我不确定如何。
  • 使用带有全局锁定的共享可写数组,沿this article行。我没有设法正确实现(示例代码非常欢迎)。

0 个答案:

没有答案