Cython boundscheck&nonech公司

2024-10-02 00:33:48 发布

您现在位置:Python中文网/ 问答频道 /正文

我为一个给我意想不到的结果的剧本挣扎了好几天。 今天,我才意识到,如果我使用一个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),但是使用这个函数上千次就足以给我在最终结果上的巨大差异。在

现在,如果删除cpc1cpc2中的nogil指令,我用c = np.zeros(4)替换{},则{}和{}函数都返回相同的结果。问题是使用ndarray而不是c array,我会损失大约50%的性能。在

在本例中,问题来自于c数组精度,但是在这种情况下,boundscheck和{}的值为什么会对结果产生影响(没有越界的访问,没有任何访问,…)

有没有办法解决这个问题?在

编辑

正如ead所强调的,如果我编译没有-03 --ffast-math -march=native的代码,cp1和{}都返回相同的结果!但代价是执行时间加倍!我或多或少能理解为什么O3ffast-math在进行激进的优化时会导致意外结果,但我不理解为什么march=native也会破坏代码。在

有没有办法既保持性能又保持精度?在


Tags: fornpcythonintixdoublecdefdmu

热门问题