使用Cython优化NumPy

pac*_*man 4 python optimization numpy cython matrix-multiplication

我目前正在尝试优化我用纯Python编写的代码.此代码使用NumPy的很沉重,因为我与NumPy的阵列工作.下面你可以看到我转换为Cython的最简单的类.这只是两个Numpy数组的乘法.这里:

bendingForces = self.matrixPrefactor * membraneHeight
Run Code Online (Sandbox Code Playgroud)

我的问题是,我是否以及如何优化它,当我看到"cython -a"生成的C代码有很多NumPy调用时,这看起来效率不高.

import numpy as np
cimport numpy as np
ctypedef np.float64_t dtype_t
ctypedef np.complex128_t cplxtype_t
ctypedef Py_ssize_t index_t

    cdef class bendingForcesClass( object ):
        cdef dtype_t bendingRigidity
        cdef np.ndarray matrixPrefactor
        cdef np.ndarray bendingForces

        def __init__( self, dtype_t bendingRigidity, np.ndarray[dtype_t, ndim=2] waveNumbersNorm ):
            self.bendingRigidity = bendingRigidity
            self.matrixPrefactor = -self.bendingRigidity * waveNumbersNorm**2

        cpdef np.ndarray calculate( self, np.ndarray membraneHeight ) :
            cdef np.ndarray bendingForces
            bendingForces = self.matrixPrefactor * membraneHeight
            return bendingForces
Run Code Online (Sandbox Code Playgroud)

我的想法是使用两个for循环并迭代数组的条目.也许我可以使用编译器来优化SIMD操作?!我试过,我可以编译,但它给出了奇怪的结果,并且永远.这是替代函数的代码:

cpdef np.ndarray calculate( self, np.ndarray membraneHeight ) :

    cdef index_t index1, index2 # corresponds to: cdef Py_ssize_t index1, index2
    for index1 in range( self.matrixSize ):
        for index2 in range( self.matrixSize ):
            self.bendingForces[ index1, index2 ] = self.matrixPrefactor.data[ index1, index2 ] * membraneHeight.data[ index1, index2 ]
    return self.bendingForces
Run Code Online (Sandbox Code Playgroud)

然而,正如我所说,这段代码非常慢,并且没有按预期运行.那么我做错了什么?什么是优化这个并删除NumPy调用操作的最佳方法?

hig*_*dth 9

对于简单的矩阵乘法,NumPy代码本身只进行循环和乘法运算,因此在Cython中很难击败它.Cython非常适合用Python替换Python中的循环的情况.您的代码比NumPy慢的原因之一是因为每次在数组中进行索引查找时,

self.bendingForces[ index1, index2 ] = self.matrixPrefactor.data[ index1, index2 ] * membraneHeight.data[ index1, index2 ]
Run Code Online (Sandbox Code Playgroud)

它做更多的计算,如边界检查(索引有效).如果将索引转换为无符号整数,则可以@cython.boundscheck(False)在函数之前使用装饰器.

有关加速Cython代码的更多详细信息,请参阅本教程.