简单cython代码的进一步优化

时间:2018-11-13 01:12:05

标签: python numpy optimization cython

我有一个用cython编写的函数,它通过double for循环计算一定程度的相关性(距离相关性):

%%cython -a
import numpy as np

def distances_Matrix(X):
    return np.array([[np.linalg.norm(xi-xj) for xi in X] for xj in X])

def c_dCov(double[:, :] a, double[:, :] b, int n):
    cdef int i
    cdef int j
    cdef double U       =  0
    cdef double W1      =  n/(n-1)
    cdef double W2      =  2/(n-2)
    cdef double[:] a_M  =  np.mean(a,axis=1)
    cdef double    a_   =  np.mean(a)
    cdef double[:] b_M  =  np.mean(b,axis=1)
    cdef double    b_   =  np.mean(b)

    for i in range(n):
        for j in range(n):
            if i != j:
                U = U + (a[i][j] + W1*(-a_M[i]-a_M[j]+a_)) * (b[i][j] +   W1*(-b_M[i]-b_M[j]+b_))
            else:
                U = U - W2*(W1**2)*(a_M[i] - a_) * (b_M[i] - b_)
    return U/(n*(n-3))

def c_dCor(X,Y):
    n     =  len(X)
    a     =  distances_Matrix(X)
    b     =  distances_Matrix(Y)
    V_XX  =  c_dCov(a,a,n) 
    V_YY  =  c_dCov(b,b,n)
    V_XY  =  c_dCov(a,b,n)
    return V_XY/np.sqrt(V_XX*V_YY)

编译这段代码时,编译器会收到以下优化报告:

enter image description here

第23行仍然是黄色,表明存在重大的python交互作用,如何使该行进一步优化?。

在那条线上完成的操作非常简单,只是乘积和求和,因为我确实指定了该函数中使用的每个数组和变量的类型,为什么我在那条线上表现不佳?

谢谢。

1 个答案:

答案 0 :(得分:1)

简短答案:c_dCov函数中的disable bounds checking,方法是在其前面的行上添加以下修饰符:

cimport cython
@cython.boundscheck(False)  # Deactivate bounds checking
def c_dCov(double[:, :] a, double[:, :] b, int n):

或者,您可以在代码顶部添加一个编译器指令。在您的Cython魔术线之后,您会输入:

%%cython -a
#cython: language_level=3, boundscheck=False

如果您有一个setup.py文件,则还可以在此处全局禁用边界检查:

from distutils.core import setup
from Cython.Build import cythonize

setup(
    name="foo",
    ext_modules=cythonize('foo.pyx', compiler_directives={'boundscheck': False}),
)

无论如何完成,仅通过禁用边界检查就足以获取以下优化报告:

enter image description here

另一些​​optimizations suggested by the Cython docs正在关闭带有负数的索引,并声明保证数组在内存中具有连续的布局。通过所有这些优化,c_dCov的签名将变为:

cimport cython
@cython.boundscheck(False)  # Deactivate bounds checking
@cython.wraparound(False)   # Deactivate negative indexing.
def c_dCov(double[:, ::1] a, double[:, ::1] b, int n):

但只需要@cython.boundscheck(False)即可获得更好的优化报告。

现在,尽管您在代码片段中没有进行这些优化,但现在我看起来更近了,但是在优化报告的代码中确实有boundscheck(False)wraparound(False)装饰器。您是否已经尝试过这些并且它们没有起作用?您正在运行什么版本的Cython?也许您需要升级。

说明

每次通过索引访问数组时,都会进行边界检查。这样,当您拥有形状为arr的数组(5,5)并尝试访问arr[19,27]时,程序将吐出一个错误,而不是让您访问边界数据。但是,为了提高速度,某些语言不对数组访问进行边界检查(例如C / C ++)。 Cython使您可以选择关闭边界检查,以优化性能。使用Cython,您可以使用boundscheck compiler directive禁用整个程序的全局边界检查,或者使用the @cython.boundscheck(False) decorator禁用单个函数的边界检查。