我试图在Python的multiprocessing
模块的帮助下并行化一些使用numpy
的计算。考虑这个简化的例子:
import time
import numpy
from multiprocessing import Pool
def test_func(i):
a = numpy.random.normal(size=1000000)
b = numpy.random.normal(size=1000000)
for i in range(2000):
a = a + b
b = a - b
a = a - b
return 1
t1 = time.time()
test_func(0)
single_time = time.time() - t1
print("Single time:", single_time)
n_par = 4
pool = Pool()
t1 = time.time()
results_async = [
pool.apply_async(test_func, [i])
for i in range(n_par)]
results = [r.get() for r in results_async]
multicore_time = time.time() - t1
print("Multicore time:", multicore_time)
print("Efficiency:", single_time / multicore_time)
当我执行它时,multicore_time
大约等于single_time * n_par
,而我希望它接近single_time
。实际上,如果我把numpy
计算替换为time.sleep(10)
,这就是我得到的——完美效率。但由于某些原因,它不能与numpy
一起工作。这个问题可以解决吗,还是numpy
的一些内部限制?在
一些有用的附加信息:
我使用的是OSX 10.9.5,Python3.4.2,CPU是CoreI7,有4个内核(虽然上面的程序总共只占CPU时间的50%,所以系统信息可能没有考虑到超线程)。
当我运行这个程序时,我看到n_par
中的n_par
进程在100%的CPU下工作
如果我将numpy
数组操作替换为循环和逐索引操作,效率将显著提高(对于n_par = 4
),效率将显著提高(大约为75%)。
提高效率的一件简单的事情应该是执行就地数组操作,如果可能,
add(a,b,a)
将而不是创建一个新数组,而a = a + b
将创建一个新数组。如果你的for循环覆盖numpy
数组可以重写为向量运算,那也应该更有效。另一种可能是使用numpy.ctypeslib
来启用共享内存numpy数组(请参见:https://stackoverflow.com/a/5550156/2379433)。在我一直在为数学编程数值方法,但也遇到了同样的问题:对于一个被认为是cpu受限的问题,我没有看到任何加速。结果我的问题是达到了CPU缓存内存限制。在
我一直在使用Intel PCM(Intel®Performance Counter Monitor)查看cpu缓存的运行情况(在Linux ksysguard中显示)。我还禁用了2个处理器以获得更清晰的结果(2个处于活动状态)。在
以下是我发现的代码:
未达到缓存内存限制的示例:
可以看出,没有缓存未命中(所有缓存命中),因此加速几乎是完美的:15/8。 Memory cache hits 2 pool
达到缓存内存限制的示例:
在本例中,我增加了我们操作的向量的大小(并减小了循环大小,以查看合理的执行时间)。在这种情况下,我们可以看到内存已满,进程总是缺少缓存内存。因此没有得到任何加速:15/15。 Memory cache misses 2 pool
观察:将操作分配给变量(aux=0.01*shared)也会使用缓存内存,并且可以通过内存绑定问题(不增加任何向量大小)。在
看起来你使用的测试函数是内存绑定的。这意味着您所看到的运行时间受到计算机将数组从内存拉入缓存的速度的限制。例如,行
a = a + b
实际上使用了3个数组,a
,b
和一个将替换a
的新数组。这三个数组每个大约8MB(1e6浮点*每个浮点8字节)。我相信不同的i7都有3MB-8MB的共享L3缓存,所以你不能一次把3个数组都放在缓存中。您的cpu添加浮点数的速度快于数组加载到缓存中的速度,因此大部分时间都花在等待从内存中读取数组。因为缓存是在核心之间共享的,所以将工作分散到多个核心上看不到任何加速。在内存受限的操作对于numpy来说是一个问题,我知道处理它们的唯一方法就是使用cython或numba之类的东西。在
相关问题 更多 >
编程相关推荐