我试图了解multiprocessing.Pool的工作原理,并且我已经开发了一个简单的例子来说明我的问题。简而言之,我使用pool.map按照示例Dead simple example of using Multiprocessing Queue, Pool and Locking并行化在数组上运行的CPU绑定函数。当我遵循这种模式时,我只获得了4个内核的适度加速,但如果我手动将数组块分成 num_threads ,然后在块上使用pool.map,我发现加速因子大大超过4x,这对我没有意义。详细信息如下。
首先,功能定义。
def take_up_time():
n = 1e3
while n > 0:
n -= 1
def count_even_numbers(x):
take_up_time()
return np.where(np.mod(x, 2) == 0, 1, 0)
现在定义我们将基准测试的功能。
首先是串行运行的函数:
def serial(arr):
return np.sum(map(count_even_numbers,arr))
现在以“标准”方式使用Pool.map的函数:
def parallelization_strategy1(arr):
num_threads = multiprocessing_count()
pool = multiprocessing.Pool(num_threads)
result = pool.map(count_even_numbers,arr)
pool.close()
return np.sum(result)
最后,第二种策略,我手动对数组进行分块,然后在块上运行Pool.map(由于python numpy split array into unequal subarrays分裂解决方案)
def split_padded(a,n):
""" Simple helper function for strategy 2
"""
padding = (-len(a))%n
if padding == 0:
return np.split(a, n)
else:
sub_arrays = np.split(np.concatenate((a,np.zeros(padding))),n)
sub_arrays[-1] = sub_arrays[-1][:-padding]
return sub_arrays
def parallelization_strategy2(arr):
num_threads = multiprocessing_count()
sub_arrays = split_padded(arr, num_threads)
pool = multiprocessing.Pool(num_threads)
result = pool.map(count_even_numbers,sub_arrays)
pool.close()
return np.sum(np.array(result))
这是我的数组输入:
npts = 1e3
arr = np.arange(npts)
现在我使用IPython%timeit函数来运行我的计时,并且对于1e3点我得到以下结果:
由于我拥有4个核心,因此策略1的速度令人失望,而且策略2可疑大于最高4倍速度。
当我将npts增加到1e4时,结果更加令人困惑:
所以两个混乱的来源是:
答案 0 :(得分:2)
结果证明您的示例非常适合Pythran模型。编译以下源代码count_even.py
:
#pythran export count_even(int [:])
import numpy as np
def count_even_numbers(x):
return np.where(np.mod(x, 2) == 0, 1, 0)
def count_even(arr):
s = 0
#omp parallel for reduction(+:s)
for elem in arr:
s += count_even_numbers(elem)
return s
使用命令行(-fopenmp激活OpenMP注释的处理):
pythran count_even.py -fopenmp
由于转换为本机代码,运行timeit
已经产生了大量的加速:
没有Pythran
$ python -m timeit -s 'import numpy as np; arr = np.arange(1e7, dtype=int); from count_even import count_even' 'count_even(arr)'
verryyy long, more than several minutes :-/
使用Pythran,一个核心
$ OMP_NUM_THREADS=1 python -m timeit -s 'import numpy as np; arr = np.arange(1e7, dtype=int); from count_even import count_even' 'count_even(arr)'
100 loops, best of 3: 10.3 msec per loop
使用Pythran,两个核心:
$ OMP_NUM_THREADS=2 python -m timeit -s 'import numpy as np; arr = np.arange(1e7, dtype=int); from count_even import count_even' 'count_even(arr)'
100 loops, best of 3: 5.5 msec per loop
快两倍,并行化工作: - )
请注意,OpenMP支持多线程,而非多处理。
答案 1 :(得分:1)
你的策略没有做同样的事情!
在第一个策略中,Pool.map
遍历数组,因此为每个数组项调用count_even_numbers
(因为数组的形状是一维的)。
第二个策略映射到数组列表,因此为列表中的每个数组调用count_even_numbers
。