我理解如何使用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_array
和map
具有不同的块大小。
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问题(最高块大小除外),并且是当前的前沿版本。
我还能怎样处理这个问题?我考虑过的想法: