我正在寻找加快以下python numpy代码的速度:
key
对应的cython代码如下:
[0, 0, 0, 0]
这是时间
def fun_np(m,data):
a, b, c = data[:,0], data[:,1], data[:,2]
M = len(data[:,0])
n = round((m+1)*(m+2)*(m+3)/6)
u =np.zeros((M,n))
C = 0
for i in range(0,m+1):
for j in range(0,i+1):
for k in range(0,j+1):
if ((i-j)!=0):
u[:,C] = (j-k)*(a)**(i-j)*(b)**(j-k-1)*(c)**k
C=C+1
return u
结果:每个循环1.97 s±11.2 ms(平均±标准偏差的7次运行,每个循环1次)
%%cython
import numpy as np
cimport numpy as np
from cython import wraparound, boundscheck, nonecheck
@boundscheck(False)
@wraparound(False)
@nonecheck(False)
cpdef fun_cyt(int m,np.ndarray[np.float64_t, ndim=2] data):
cdef:
np.ndarray[np.float64_t, ndim=1] a = data[:,0]
np.ndarray[np.float64_t, ndim=1] b = data[:,1]
np.ndarray[np.float64_t, ndim=1] c = data[:,2]
int M, n
Py_ssize_t i, j, k, s
M = len(data[:,0])
n = round((m+1)*(m+2)*(m+3)/6)
cdef np.ndarray[np.float64_t, ndim=2] u = np.zeros((M,n), dtype=np.float64)
cdef int C = 0
for i in range(m+1): #range(0,m+1):
for j in range(i+1):
for k in range(j+1):
for s in range(M):
if (i-j)!=0:
u[s,C] = (j-k)*(a[s])**(i-j)*(b[s])**(j-k-1)*(c[s])**k
C=C+1
return u
结果:每个循环1.91 s±12.7 ms(平均±标准偏差,共7次运行,每个循环1次)
如您所见,numpy和cython代码之间没有显着的速度。如果可以的话,如果您能帮助优化cython代码,我将不胜感激。
cython代码html的带注释的html
答案 0 :(得分:1)
正如评论中已经提到的,您可以使用numba进行尝试。我建议进一步并行化循环:
from numba import prange, jit
@jit(nopython=True, parallel=True)
def fun_numba(m,data):
a, b, c = data[:,0], data[:,1], data[:,2]
M = len(data[:,0])
n = round((m+1)*(m+2)*(m+3)/6)
u = np.zeros((M,n))
C = 0
for i in range(0,m+1):
for j in range(0,i+1):
for k in prange(0,j+1):
if ((i-j)!=0):
u[:,C] = (j-k)*(a)**(i-j)*(b)**(j-k-1)*(c)**k
C=C+1
return u
在我的机器上给我
In [11]: %timeit fun_np(m,z)
642 ms ± 4.13 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [12]: %timeit fun_numba(m,z)
101 ms ± 7.15 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
答案 1 :(得分:1)
非常有趣的例子! 大多数操作都在6000个元素向量上。 Cython在大矢量乘方,乘法和加法方面确实不能比numpy快。 通过在Cython中实现此功能,您可能与numpy一样快,甚至通过消除一些numpy的开销,甚至可以获得10%到20%的利润。
但是,还有其他方法可以加快计算速度。 向量运算是对数据向量的三列进行的操作,您将写入输出向量的列。 默认情况下,numpy数组具有以行为主的顺序,即在内存中行在内存中是连续的。 对于此处执行的操作,这是不好的。 进一步阅读:https://en.wikipedia.org/wiki/Row-_and_column-major_order。
这两个函数基本上是相同的,如果在函数之外创建输出矢量,则它们将是相同的。
请注意以下几点:我用u [:,C] + =替换了u [:,C] = ...,因为否则结果仅由k = j定义,因此始终为0。 我不知道这些计算的意义是什么,但这可能不是。
import numpy as np
def fun_np(m,data):
a, b, c = data[:,0], data[:,1], data[:,2]
M = len(data[:,0])
n = round((m+1)*(m+2)*(m+3)/6)
u = np.zeros((M,n))
C = 0
for i in range(0,m+1):
for j in range(0,i+1):
for k in range(0,j+1):
if ((i-j)!=0):
u[:,C] += (j-k)*(a)**(i-j)*(b)**(j-k-1)*(c)**k
C=C+1
return u
def fun_npF(m,data):
a, b, c = data[:,0], data[:,1], data[:,2]
M = len(data[:,0])
n = round((m+1)*(m+2)*(m+3)/6)
u = np.zeros((M,n),order='F')
C = 0
for i in range(0,m+1):
for j in range(0,i+1):
for k in range(0,j+1):
if ((i-j)!=0):
u[:,C] += (j-k)*(a)**(i-j)*(b)**(j-k-1)*(c)**k
C=C+1
return u
z = np.random.randn(6000, 3); m=20;
print("Numpy Row-major")
%timeit fun_np(m,z);
# Fortran order, because vector operations on columns
print("Numpy Column-major")
zF = np.asarray(z.copy(),order='F')
%timeit fun_npT(m,zF);
# Check if output the same
diff = (max(np.ravel(abs(fun_np(m,z)-fun_npF(m,zF)))))
max_rm = (max(np.ravel(abs(fun_np(m,z)))))
max_cm = (max(np.ravel(abs(fun_npF(m,zF)))))
print("Dffference: %f, Max value Row-major: %f, Max value Column-major: %f"%(diff, max_rm, max_cm))
这给了我
Numpy Row-major
1.64 s ± 12.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Numpy Column-major
16 ms ± 345 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Dffference: 0.000000, Max value Row-major: 196526643123.792450, Max value Column-major: 196526643123.792450
在考虑将“ if”放在哪里并将其与Cython结合使用时,您可以获得甚至更多的收益,但是我猜只能再增加10%到20%。