【发布时间】:2016-02-18 20:04:58
【问题描述】:
我试图让 python 中的循环尽可能快地运行。所以我深入研究了 NumPy 和 Cython。 这是原始 Python 代码:
def calculate_bsf_u_loop(uvel,dy,dz):
"""
Calculate barotropic stream function from zonal velocity
uvel (t,z,y,x)
dy (y,x)
dz (t,z,y,x)
bsf (t,y,x)
"""
nt = uvel.shape[0]
nz = uvel.shape[1]
ny = uvel.shape[2]
nx = uvel.shape[3]
bsf = np.zeros((nt,ny,nx))
for jn in range(0,nt):
for jk in range(0,nz):
for jj in range(0,ny):
for ji in range(0,nx):
bsf[jn,jj,ji] = bsf[jn,jj,ji] + uvel[jn,jk,jj,ji] * dz[jn,jk,jj,ji] * dy[jj,ji]
return bsf
这只是 k 个索引的总和。数组大小为 nt=12、nz=75、ny=559、nx=1442,因此约 7.25 亿个元素。 那花了68秒。现在,我已经在 cython 中完成了
import numpy as np
cimport numpy as np
cimport cython
@cython.boundscheck(False) # turn off bounds-checking for entire function
@cython.wraparound(False) # turn off negative index wrapping for entire function
## Use cpdef instead of def
## Define types for arrays
cpdef calculate_bsf_u_loop(np.ndarray[np.float64_t, ndim=4] uvel, np.ndarray[np.float64_t, ndim=2] dy, np.ndarray[np.float64_t, ndim=4] dz):
"""
Calculate barotropic stream function from zonal velocity
uvel (t,z,y,x)
dy (y,x)
dz (t,z,y,x)
bsf (t,y,x)
"""
## cdef the constants
cdef int nt = uvel.shape[0]
cdef int nz = uvel.shape[1]
cdef int ny = uvel.shape[2]
cdef int nx = uvel.shape[3]
## cdef loop indices
cdef ji,jj,jk,jn
## cdef. Note that the cdef is followed by cython type
## but the np.zeros function as python (numpy) type
cdef np.ndarray[np.float64_t, ndim=3] bsf = np.zeros([nt,ny,nx], dtype=np.float64)
for jn in xrange(0,nt):
for jk in xrange(0,nz):
for jj in xrange(0,ny):
for ji in xrange(0,nx):
bsf[jn,jj,ji] += uvel[jn,jk,jj,ji] * dz[jn,jk,jj,ji] * dy[jj,ji]
return bsf
这需要 49 秒。 但是,将循环换成
for jn in range(0,nt):
for jk in range(0,nz):
bsf[jn,:,:] = bsf[jn,:,:] + uvel[jn,jk,:,:] * dz[jn,jk,:,:] * dy[:,:]
只需 0.29 秒!不幸的是,我不能在我的完整代码中做到这一点。
为什么 NumPy 的切片速度比 Cython 循环快得多? 我认为 NumPy 很快,因为它是 Cython。那么它们不应该具有相似的速度吗?
如您所见,我在 cython 中禁用了边界检查,并且还使用“快速数学”进行了编译。但是,这只会带来很小的加速。 无论如何要让循环与 NumPy 切片具有相似的速度,还是循环总是比切片慢?
非常感谢任何帮助! /乔金
【问题讨论】:
-
您忘记声明循环变量的类型。
-
当前 Cython 版本中无需声明循环变量。
-
@jakevdp 在这种情况下,不声明它们似乎可以让它正确推断类型,但将它们声明为
cdef ji,jj,jk,jn而没有类型似乎会将其丢弃。 -
对 - 我的意思是
cdef ji, jj, jk, jn行可以完全删除,这可能会缩短执行时间。 -
非常感谢!将循环索引声明为
cdef int ji,jj,jk,jn会有所不同!
标签: python arrays performance numpy cython