Индексация массива Cython - PullRequest
       9

Индексация массива Cython

4 голосов
/ 16 сентября 2011

Я пытаюсь ускорить некоторый код Python с помощью Cython, и я использую опцию -a в Cython, чтобы посмотреть, где я могу что-то улучшить.Насколько я понимаю, в сгенерированном html-файле выделенные строки - это те, где вызываются функции python - это правильно?

В следующей тривиальной функции я объявил аргумент numpy array arr, используя буферсинтаксис.Я подумал, что это позволяет выполнять операции индексации исключительно в C без необходимости вызова функций Python.Однако cython -a (версия 0.15) выделяет строку, где я установил значение элемента arr, но не ту, где я читал один из его элементов.Почему это происходит?Есть ли более эффективный способ доступа к элементам массива numpy?

import numpy
cimport numpy

def foo(numpy.ndarray[double, ndim=1] arr not None):
    cdef int i
    cdef double elem
    for i in xrange(10):
      elem = arr[i]          #not highlighted
      arr[i] = 1.0 + elem    #highlighted

РЕДАКТИРОВАТЬ: Кроме того, как буферный аргумент mode взаимодействует с numpy?Предполагая, что я не изменил order аргумент numpy.array по умолчанию, всегда ли безопасно использовать mode='c'?Это на самом деле влияет на производительность?

РЕДАКТИРОВАТЬ после комментария Делнана: arr[i] += 1 также выделяется (вот почему я разделил его в первую очередь, чтобы увидеть, какая часть операции вызывала проблему).Если я отключаю проверку границ для упрощения (это не имеет значения для того, что подсвечивается), сгенерированный код c:

  /* "ct.pyx":11
 *   cdef int i
 *   cdef double elem
 *   for i in xrange(10):             # <<<<<<<<<<<<<<
 *     elem = arr[i]
 *     arr[i] = 1.0 + elem
 */
  for (__pyx_t_1 = 0; __pyx_t_1 < 10; __pyx_t_1+=1) {
    __pyx_v_i = __pyx_t_1;

    /* "ct.pyx":12
 *   cdef double elem
 *   for i in xrange(10):
 *     elem = arr[i]             # <<<<<<<<<<<<<<
 *     arr[i] = 1.0 + elem
 */
    __pyx_t_2 = __pyx_v_i;
    __pyx_v_elem = (*__Pyx_BufPtrStrided1d(double *, __pyx_bstruct_arr.buf, __pyx_t_2, __pyx_bstride_0_arr));

    /* "ct.pyx":13
 *   for i in xrange(10):
 *     elem = arr[i]
 *     arr[i] = 1.0 + elem             # <<<<<<<<<<<<<<
 */
    __pyx_t_3 = __pyx_v_i;
    *__Pyx_BufPtrStrided1d(double *, __pyx_bstruct_arr.buf, __pyx_t_3, __pyx_bstride_0_arr) = (1.0 + __pyx_v_elem);
  }

1 Ответ

4 голосов
/ 16 сентября 2011

Ответ в том, что маркер обманывает читателя.Я скомпилировал ваш код, и инструкции, сгенерированные под выделением, - это те, которые необходимы для обработки случаев ошибки и возвращаемого значения, они не связаны с назначением массива.

Действительно, если вы измените код на чтение:

def foo(numpy.ndarray[double, ndim=1] arr not None):
    cdef int i
    cdef double elem
    for i in xrange(10):
      elem = arr[i]
      arr[i] = 1.0 + elem
    return # + add this

Выделение будет в последней строке и не более в назначении.

Вы можете еще больше ускорить ваш код, используя @ cython.boundscheck:

import numpy
cimport numpy
cimport cython

@cython.boundscheck(False)
def foo(numpy.ndarray[double, ndim=1] arr not None):
    cdef int i
    cdef double elem
    for i in xrange(10):
      elem = arr[i]
      arr[i] = 1.0 + elem
    return 
...