使用python多处理优化一个简单的CPU绑定函数
我试图理解multiprocessing.Pool是如何工作的,我开发了一个简单的示例来说明我的问题。简单地说,我使用pool.map通过以下示例并行化在阵列上运行的CPU绑定函数。当我遵循这种模式时,我只有4个内核的适度加速,但是如果我手动将数组分块到num_线程中,然后在这些分块上使用pool.map,我会发现加速因子远远超过4倍,这对我来说毫无意义。详情如下 首先,函数定义使用python多处理优化一个简单的CPU绑定函数,python,performance,numpy,parallel-processing,python-multiprocessing,Python,Performance,Numpy,Parallel Processing,Python Multiprocessing,我试图理解multiprocessing.Pool是如何工作的,我开发了一个简单的示例来说明我的问题。简单地说,我使用pool.map通过以下示例并行化在阵列上运行的CPU绑定函数。当我遵循这种模式时,我只有4个内核的适度加速,但是如果我手动将数组分块到num_线程中,然后在这些分块上使用pool.map,我会发现加速因子远远超过4倍,这对我来说毫无意义。详情如下 首先,函数定义 def take_up_time(): n = 1e3 while n > 0:
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的函数:
最后是第二种策略,我手动将数组分块,然后在分块上运行Pool.map(由于
这是我的数组输入:
npts = 1e3
arr = np.arange(npts)
现在我使用IPython%timeit函数来运行计时,对于1e3点,我得到以下结果:
- 串行:10个环路,每个环路3:98.7毫秒的最佳时间
- 并行化策略1:10个循环,每个循环的最佳时间为3:77.7毫秒
- 并行化策略2:10个循环,每个循环最好3:22毫秒
- 串行:1个循环,每个循环最好3:967毫秒
- 并行化策略1:1个循环,每个循环最好3:596毫秒
- 并行化策略2:10个循环,每个循环的最佳时间为3:22.9毫秒
你的策略不一样 在第一种策略中,
Pool.map
迭代一个数组,因此为每个数组项调用count\u偶数
(因为数组的形状是一维的)
第二个策略映射到一个数组列表上,因此对列表中的每个数组调用
计数偶数。您的策略不一样
在第一种策略中,Pool.map
迭代一个数组,因此为每个数组项调用count\u偶数
(因为数组的形状是一维的)
第二种策略映射到一个数组列表,因此对列表中的每个数组调用计数偶数。结果表明,您的示例非常适合该模型。编译以下源代码count\u偶数.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激活注释的处理):
由于转换为本机代码,在此基础上运行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 :-/
使用Pytran,一个核心
$ 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
配备Pyran,两个磁芯:
$ 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支持多线程,而不是多处理。结果表明,您的示例非常适合该模型。编译以下源代码count\u偶数.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激活注释的处理):
由于转换为本机代码,在此基础上运行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 :-/
使用Pytran,一个核心
$ 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
配备Pyran,两个磁芯:
$ 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支持多线程,而不是多处理。您是否比较了不同策略的结果?您是否比较了不同策略的结果?罗兰,这一点很好,解决了这个谜团,事后看来很明显。谢谢罗兰,这是一个很好的观点,它解决了这个谜,而且在事后看来是显而易见的。谢谢