Цитонизированная функция неожиданно медленная

Я хотел ускорить функцию, которую часто использую, и думал об использовании cython. Однако, попробовав все возможные оптимизации cython, которые мне удалось найти в документации, код cython примерно в 6 раз медленнее, чем функция python+numpy. Разочаровывает!

Это мой тестовый код: (forward1 — функция python, forward2 — функция cython)

#geometry.py
def forward1(points, rotation, translation):
    '''points are in columns'''
    return np.dot(rotation, points - translation[:, np.newaxis])

#geometry.pyx
import numpy as np
cimport numpy as np
cimport cython

@cython.boundscheck(False)
@cython.wraparound(False)
@cython.nonecheck(False)
cdef np.float64_t[:,:] forward2(np.float64_t[:,:] points, np.float64_t[:,:] rotation, np.float64_t[:] translation):
    '''points are in columns'''
    cdef unsigned int I, J
    I = points.shape[0]
    J = points.shape[1]
    cdef np.float64_t[:,:] tmp = np.empty((I, J), dtype=np.float64)
    cdef unsigned int i
    for i in range(J):
        tmp[0, i] = points[0, i] - translation[0]        
        tmp[1, i] = points[1, i] - translation[1]        
    cdef np.float64_t[:,:] result = np.dot(rotation, tmp)
    return result

def test_forward2(points, rotation, translation):
    import timeit
    cdef np.float64_t[:,:] points2 = points
    cdef np.float64_t[:,:] rotation2 = rotation
    cdef np.float64_t[:] translation2 = translation
    t = timeit.Timer(lambda: forward2(points2, rotation2, translation2))
    print min(t.repeat(3, 10))

и затем я время это:

t = timeit.Timer(lambda: forward1(points, rotation, translation))
print min(t.repeat(3, 10))
0.000368164520751

test_forward2(points, rotation, translation)
0.0023365181969

Могу ли я что-нибудь сделать с кодом cython, чтобы сделать его быстрее?

Если forward1 нельзя ускорить в cython, могу ли я надеяться на ускорение с помощью weave?

ИЗМЕНИТЬ:

Просто для протокола: еще одна вещь, которую я пытался ускорить, - это передача точек в порядке фортрана, поскольку мои точки хранятся в столбцах, и их довольно много. Я также определяю локальный tmp как порядок fortran. Я думаю, что часть вычитания функции должна быть быстрее, но numpy.dot, похоже, требует вывода порядка C (в любом случае, чтобы обойти это?), Так что в целом с этим тоже нет ускорения. Я также попытался переставить точки так, чтобы часть вычитания выполнялась быстрее в порядке C, но кажется, что скалярное произведение по-прежнему является самой дорогой частью.

Кроме того, я заметил, что numpy.dot не может использовать представления памяти в качестве аргумента out, даже если это порядок C, это ошибка?


person martinako    schedule 13.10.2012    source источник
comment
numpy уже использует для расчетов библиотеки c и fortran. обычно вам не нужно ничего делать, чтобы получить ускорение.   -  person none    schedule 14.10.2012


Ответы (1)


Просто взглянув на ваш код, он выглядит как нечто (вычитание массивов и скалярное произведение), для которого numpy уже очень оптимизирован.

Cython отлично подходит для ускорения случаев, когда numpy часто работает плохо (например, итерационные алгоритмы, где итерация написана на python), но в этом случае внутренний цикл уже предварительно формируется библиотекой BLAS.

Если вы хотите ускорить процесс, в первую очередь я бы посмотрел, с чем связаны библиотеки BLAS/LAPACK/ATLAS/etc. Использование «настроенной» библиотеки линейной алгебры (например, ATLAS или Intel MKL) даст большую (в некоторых случаях> 10x) разницу в подобных случаях.

Чтобы узнать, что вы сейчас используете, посмотрите на вывод numpy.show_config()

person Joe Kington    schedule 13.10.2012
comment
Хорошо, спасибо, я понял. Я запустил numpy.show_config(), но не знаю, как интерпретировать результаты: ‹br/› mkl_info: libraries = ['mkl_lapack95', 'mkl_blas95', 'mkl_intel_c', ...] library_dirs = ['C:/Program Files (x86)/Intel/Compiler/11.1/070/mkl/ia32/lib'] define_macros = [('SCIPY_MKL_H', None)] include_dirs = ['C:/Program Files (x86)/Intel/Compiler/11.1/070/mkl/include'] Я получаю аналогичную структуру для lapack_opt_info, blas_opt_info, lapack_mkl_info и blas_mkl_info. Значит ли это, что моя версия numpy была связана с MKL? или [('SCIPY_MKL_H', None)] означает, что это не так? - person martinako; 14.10.2012
comment
Для справки, моя версия numpy - «1.6.2», и я установил ее с бесплатным распространением EPD в окне Windows7. - person martinako; 14.10.2012
comment
Да, ваш numpy связан с MKL. Тогда мое предложение вам не сильно поможет. MKL максимально оптимизирован. - person Joe Kington; 14.10.2012
comment
Еще одна мысль: вы обычно вызываете это много раз для небольших входных массивов? При вызове любой конкретной функции numpy возникает много накладных расходов. Если вы перебираете больший массив и вызываете эту функцию много раз, вы можете получить ускорение, используя tensordot или einsum и работая со всем большим массивом одновременно. Конечно, если вы не перебираете больший массив, это никому не поможет. - person Joe Kington; 14.10.2012
comment
Хорошо спасибо. Я вызываю эту функцию с сотнями точек, и это часть целевой функции в процессе минимизации. На самом деле, это минимизация, о которой я говорю scipy-function">здесь Я думаю, что не могу собрать все точки воедино, так как минимизация является последовательной. - person martinako; 15.10.2012
comment
Да, для задачи минимизации вам определенно нужны последовательные вызовы. В этом случае лучше попытаться уменьшить количество вызовов целевой функции. Вы можете рассмотреть возможность использования алгоритма оптимизации, который не требует вычисления градиента на каждом шаге (например, метод Пауэлла), так как это (обычно) уменьшит количество вызовов целевой функции. Кроме того, если вообще возможно сделать лучшее начальное предположение, это может значительно сократить количество итераций минимизации. Вы также можете рассмотреть возможность ослабления допусков минимизации. - person Joe Kington; 15.10.2012