我正在使用具有8G RAM和Linux Redhat 7的8核处理器机器,并且正在使用Pycharm IDE。
我试图使用python线程模块来利用多核处理的优势,但最终却得到了慢得多的代码。我通过Numba发布了GIL,并确保我的线程正在执行足够复杂的计算,因此问题不在How to make numba @jit use all cpu cores (parallelize numba @jit)
中讨论过这是多线程代码:
l=200
@nb.jit('void(f8[:],f8,i4,f8[:])',nopython=True,nogil=True)
def force(r,ri,i,F):
sum=0
for j in range(12):
if (j != i):
fij=-4 * (12*1**12/(r[j]-ri)**13-6*1**6/(r[j]-ri)**7)
sum=sum+fij
F[i+12]=sum
def ODEfunction(r, t):
f = np.zeros(2 * 12)
lbound=-4* (12*1**12/(-0.5*l-r[0])**13-6*1**6/(-0.5*l-r[0])**7)
rbound=-4* (12*1**12/(0.5*l-r[12-1])**13-6*1**6/(0.5*l-r[12-1])**7)
f[0:12]=r[12:2*12]
thlist=[threading.Thread(target=force, args=(r,r[i],i,f)) for i in range(12)]
for thread in thlist:
thread.start()
for thread in thlist:
thread.join()
f[12]=f[12]+lbound
f[2*12-1]=f[2*12-1]+rbound
return f
这是顺序版本:
l=200
@nb.autojit()
def ODEfunction(r, t):
f = np.zeros(2 * 12)
lbound=-4* (12*1**12/(-0.5*l-r[0])**13-6*1**6/(-0.5*l-r[0])**7)
rbound=-4* (12*1**12/(0.5*l-r[12-1])**13-6*1**6/(0.5*l-r[12-1])**7)
f[0:12]=r[12:2*12]
for i in range(12):
fi = 0.0
for j in range(12):
if (j!=i):
fij = -4 * (12*1**12/(r[j]-r[i])**13-6*1**6/(r[j]-r[i])**7)
fi = fi + fij
f[i+12]=fi
f[12]=f[12]+lbound
f[2*12-1]=f[2*12-1]+rbound
return f
我还考虑了在多线程和顺序代码运行期间附加系统监视器的图像:
System Motinor during the run of the multi threaded code
System Motinor during the run of the sequential code
有人知道导致线程代码效率低下的原因是什么吗?
答案 0 :(得分:2)
您必须知道,在Python中调用一个函数的成本相当高(例如,与在C中调用一个函数相比),因此调用numba-jitted函数的代价更高:必须检查参数是否正确(即它们确实是您要传递的float-numpy数组-我们将看到它比普通的Python调用慢5倍。)
与函数中发生的工作相比,让我们检查一下jitted函数的开销:
import numba as nb
import numpy as np
@nb.jit('void(f8[:],f8,i4,f8[:])',nopython=True,nogil=True)
def force(r,ri,i,F):
sum=0
for j in range(12):
if (j != i):
fij=-4 * (12*1**12/(r[j]-ri)**13-6*1**6/(r[j]-ri)**7)
sum=sum+fij
F[i+12]=sum
@nb.jit('void(f8[:],f8,i4,f8[:])',nopython=True,nogil=True)
def nothing(r,ri,i,F):
pass
def no_jit(r,ri,i,F):
pass
F=np.zeros(24)
r=np.zeros(12)
现在:
>>>%timeit force(r,1.0,0,F)
706 ns ± 8.96 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
>>>%timeit nothing(r,1.0,0,F)
645 ns ± 5.36 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
>>>%timeit no_jit(r,1.0,0,F) #to measure overhead of numba
120 ns ± 6.56 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
因此,基本上有90%的开销,因为该函数是“内联的”,所以您在单线程模式下没有这些开销。这并不奇怪:您的for循环只有12次迭代-只是工作量不够,例如,在您已链接内部循环的示例中,有10^10
个迭代!
此外,在线程之间分派工作也有一些开销,我的胆量说,这甚至比jitted-call的开销还多-但要确保应该对程序进行概要分析。即使使用8核,这些赔率也很难克服!
与功能本身所花费的时间相比,目前最大的路障可能是force
调用的大开销。上面的分析是很肤浅的,所以我不保证没有其他重要的问题-但是为force
做更多的工作将是朝着正确方向迈出的一步。