我为一个给我意想不到的结果的剧本挣扎了好几天。
今天,我才意识到,如果我使用一个cython函数,无论是否有boundscheck
和{
下面是一个例子:
import numpy as np
cimport numpy as np
cimport cython
cdef double[4] c
c[0] = 0.1
c[1] = 0.2
c[2] = 0.3
c[3] = 0.4
def cp1(double[:,::1] u, double[:,::1] K, int ixmin, int ixmax, int izmin, int izmax):
cpc1(u, K, ixmin, ixmax, izmin, izmax)
def cp2(double[:,::1] u, double[:,::1] K, int ixmin, int ixmax, int izmin, int izmax):
cpc2(u, K, ixmin, ixmax, izmin, izmax)
@cython.boundscheck(False)
@cython.nonecheck(False)
cdef void cpc1(double[:,::1] u, double[:,::1] K, int ixmin, int ixmax, int izmin, int izmax) nogil:
cdef Py_ssize_t ix, iz
cdef double dpu, dmu
for ix in range(ixmin+2, ixmax-1):
for iz in range(izmin, izmax):
dpu = c[0]*u[ix-1, iz] + c[1]*u[ix, iz] + c[2]*u[ix+1, iz]
dmu = c[1]*u[ix-1, iz] + c[2]*u[ix, iz] + c[3]*u[ix+1, iz]
K[ix, iz] = 0.5*dpu - 0.5*dmu
@cython.boundscheck(True)
@cython.nonecheck(True)
cdef void cpc2(double[:,::1] u, double[:,::1] K, int ixmin, int ixmax, int izmin, int izmax) nogil:
cdef Py_ssize_t ix, iz
cdef double dpu, dmu
for ix in range(ixmin+2, ixmax-1):
for iz in range(izmin, izmax):
dpu = c[0]*u[ix-1, iz] + c[1]*u[ix, iz] + c[2]*u[ix+1, iz]
dmu = c[1]*u[ix-1, iz] + c[2]*u[ix, iz] + c[3]*u[ix+1, iz]
K[ix, iz] = 0.5*dpu - 0.5*dmu
如果我运行这些线路:
^{pr2}$指令np.all(K1 == K2)
返回False
。两个数组之间的差异接近于机器精度(大约5e-17),但是使用这个函数上千次就足以给我在最终结果上的巨大差异。在
现在,如果删除cpc1
和cpc2
中的nogil
指令,我用c = np.zeros(4)
替换{ndarray
而不是c array
,我会损失大约50%的性能。在
在本例中,问题来自于c数组精度,但是在这种情况下,boundscheck
和{
有没有办法解决这个问题?在
编辑
正如ead所强调的,如果我编译没有-03 --ffast-math -march=native
的代码,cp1
和{O3
和ffast-math
在进行激进的优化时会导致意外结果,但我不理解为什么march=native
也会破坏代码。在
有没有办法既保持性能又保持精度?在
目前没有回答
相关问题 更多 >
编程相关推荐