我目前正在尝试优化我用纯Python编写的代码。当我使用NumPy数组时,此代码使用NumPy。您可以在下面看到我转换为Cython的最简单的类。这只是两个Numpy数组的乘法。这里:
bendingForces = self.matrixPrefactor * membraneHeight
我的问题是,我是否以及如何优化它,当我看到“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
我的想法是使用两个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
然而,正如我所说,这段代码非常慢,并且没有按预期运行。那么我做错了什么?优化此操作并删除NumPy调用操作的最佳方法是什么?
答案 0 :(得分:9)
对于简单的矩阵乘法,NumPy代码本身只进行循环和乘法运算,因此在Cython中很难超越它。 Cython非常适合用Python替换Python中的循环的情况。您的代码比NumPy慢的原因之一是因为每次在数组中进行索引查找时,
self.bendingForces[ index1, index2 ] = self.matrixPrefactor.data[ index1, index2 ] * membraneHeight.data[ index1, index2 ]
它执行更多计算,如边界检查(索引有效)。如果将索引转换为无符号整数,则可以在函数前使用装饰器@cython.boundscheck(False)
。
有关加速Cython代码的更多详细信息,请参阅此tutorial。
答案 1 :(得分:0)