Оптимизация NumPy с помощью Cython - PullRequest
4 голосов
/ 16 марта 2011

В настоящее время я пытаюсь оптимизировать код, написанный на чистом Python. Этот код очень интенсивно использует NumPy , так как я работаю с массивами NumPy. Ниже вы можете увидеть самые простые из моих классов, которые я конвертировал в Cython . Который делает только умножение двух массивов Numpy. Здесь:

bendingForces = self.matrixPrefactor * membraneHeight

Мой вопрос заключается в том, можно ли и как это оптимизировать, когда я смотрю на C-код, который генерирует "cython -a", имеет много 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?

Ответы [ 2 ]

9 голосов
/ 17 марта 2011

Для простых умножений матриц код NumPy уже выполняет только циклическое и умножение, поэтому было бы трудно обойти это в Cython.Cython отлично подходит для ситуаций, когда вы заменяете циклы в Python циклами в Cython.Одна из причин, по которой ваш код работает медленнее, чем NumPy, заключается в том, что каждый раз, когда вы выполняете поиск индекса в вашем массиве,

self.bendingForces[ index1, index2 ] = self.matrixPrefactor.data[ index1, index2 ] * membraneHeight.data[ index1, index2 ]

он выполняет больше вычислений, таких как проверка границ (индекс действителен).Если вы приведете свои индексы к целым числам без знака, вы можете использовать декоратор @cython.boundscheck(False) перед функцией.

См. Этот учебник для получения дополнительной информации по ускорению кода Cython.

0 голосов
/ 20 марта 2011

Вероятно, вы могли бы ускорить это, используя

for index1 from 0 <= index1 < max1:

вместо того, чтобы использовать диапазон, я не уверен, что напечатан.

Вы проверяли это и это ?

...