在cython中缓慢分裂

Nea*_*hes 13 python cython

为了在cython中获得快速划分,我可以使用编译器指令

@cython.cdivision(True)
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为得到的c代码没有零分割检查.但是由于某些原因,它实际上使我的代码变慢.这是一个例子:

@cython.boundscheck(False)
@cython.wraparound(False)
@cython.nonecheck(False)
@cython.cdivision(True)
def example1(double[:] xi, double[:] a, double[:] b, int D):

    cdef int k
    cdef double[:] x = np.zeros(D)

    for k in range(D):
        x[k] = (xi[k] - a[k]) / (b[k] - a[k]) 

    return x

@cython.boundscheck(False)
@cython.wraparound(False)
@cython.nonecheck(False)
def example2(double[:] xi, double[:] a, double[:] b, int D):

    cdef int k
    cdef double[:] x = np.zeros(D)

    for k in range(D):
        x[k] = (xi[k] - a[k]) / (b[k] - a[k]) 

    return x

def test_division(self):

    D = 10000
    x = np.random.rand(D)
    a = np.zeros(D)
    b = np.random.rand(D) + 1

    tic = time.time()
    example1(x, a, b, D)
    toc = time.time()

    print 'With c division: ' + str(toc - tic)

    tic = time.time()
    example2(x, a, b, D)
    toc = time.time()

    print 'Without c division: ' + str(toc - tic)
Run Code Online (Sandbox Code Playgroud)

这导致输出:

With c division: 0.000194787979126
Without c division: 0.000176906585693
Run Code Online (Sandbox Code Playgroud)

是否有任何理由为什么关闭零分割检查会减慢事情(我知道没有零除数).

小智 12

首先,您需要多次调用函数(> 1000),并平均花费在每个函数上的时间,以准确了解它们的不同之处.每次调用每个函数都不够准确.

其次,在函数中花费的时间将受到其他事物的影响,而不仅仅是具有分割的循环.调用这样的defPython函数涉及传递和返回参数的一些开销.此外,在函数中创建一个numpy数组需要时间,因此两个函数中循环的任何差异都不太明显.

最后,请参阅此处(https://github.com/cython/cython/wiki/enhancements-compilerdirectives),将c-division指令设置为False具有~35%的速度惩罚.考虑到其他开销,我认为这还不足以显示在您的示例中.我检查了CythonC代码输出,并且example2的代码明显不同,并且包含额外的零分区检查,但是当我对其进行分析时,运行时的差异可以忽略不计.

为了说明这一点,我运行了下面的代码,在那里我已经使用了代码并将def函数转换为cdef函数,即Cython函数而不是Python函数.这大大减少了传递和返回参数的开销.我还改变了example1example2来计算numpy数组中的值的总和,而不是创建一个新数组并填充它.这意味着几乎所有在每个函数中花费的时间现在都在循环中,因此应该更容易看到任何差异.我也多次运行每个功能,并使D更大.

@cython.boundscheck(False)
@cython.wraparound(False)
@cython.nonecheck(False)
@cython.cdivision(True) 
@cython.profile(True)
cdef double example1(double[:] xi, double[:] a, double[:] b, int D):

    cdef int k
    cdef double theSum = 0.0

    for k in range(D):
        theSum += (xi[k] - a[k]) / (b[k] - a[k])

    return theSum

@cython.boundscheck(False)
@cython.wraparound(False)
@cython.nonecheck(False)
@cython.profile(True)
@cython.cdivision(False)
cdef double example2(double[:] xi, double[:] a, double[:] b, int D):

    cdef int k
    cdef double theSum = 0.0

    for k in range(D):
        theSum += (xi[k] - a[k]) / (b[k] - a[k])

    return theSum


def testExamples():
    D = 100000
    x = np.random.rand(D)
    a = np.zeros(D)
    b = np.random.rand(D) + 1

    for i in xrange(10000):
        example1(x, a, b, D)
        example2(x, a, b,D) 
Run Code Online (Sandbox Code Playgroud)

我通过探查器(python -m cProfile -s累积)运行此代码,相关输出如下:

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
10000    1.546    0.000    1.546    0.000 test.pyx:26(example2)
10000    0.002    0.000    0.002    0.000 test.pyx:11(example1)
Run Code Online (Sandbox Code Playgroud)

这表明example2慢得多.如果我在example2中打开c-division,那么对于example1和example2花费的时间是相同的,所以这显然具有显着的效果.