установить массив расширения cdef для Cython на ноль - PullRequest
0 голосов
/ 30 апреля 2018

Есть ли Cython-ic способ установить массив cdef в нули. У меня есть функция со следующей подписью:

cdef cget_values(double[:] cpc_x, double[:] cpc_y):

Функция вызывается следующим образом:

cdef double cpc_x [16]
cdef double cpc_y [16]
cget_values(cpc_x, cpc_y)

Теперь первое, что я хотел бы сделать, это установить все в этих массивах в нули. В настоящее время я делаю это с помощью цикла for:

for i in range(16):
    cpc_x[i] = 0.0
    cpc_y[i] = 0.0

Мне было интересно, если это разумный подход без особых накладных расходов. Я часто называю эту функцию, и мне было интересно, есть ли более элегантный / более быстрый способ сделать это в Cython.

1 Ответ

0 голосов
/ 30 апреля 2018

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

Для удобства чтения я бы использовал:

cpc_x[:]=0.0
cpc_y[:]=0.0

цитон перевел бы это на for -циклы. Другое дополнительное преимущество: даже если @cython.boundscheck(False) не используется, полученный C-код, тем не менее, не будет иметь проверок (__Pyx_RaiseBufferIndexError). Вот результирующий код для a[:]=0.0:

  {
      double __pyx_temp_scalar = 0.0;
      {
          Py_ssize_t __pyx_temp_extent_0 = __pyx_v_a.shape[0];
          Py_ssize_t __pyx_temp_stride_0 = __pyx_v_a.strides[0];
          char *__pyx_temp_pointer_0;
          Py_ssize_t __pyx_temp_idx_0;
          __pyx_temp_pointer_0 = __pyx_v_a.data;
          for (__pyx_temp_idx_0 = 0; __pyx_temp_idx_0 < __pyx_temp_extent_0; __pyx_temp_idx_0++) {
            *((double *) __pyx_temp_pointer_0) = __pyx_temp_scalar;
            __pyx_temp_pointer_0 += __pyx_temp_stride_0;
          }
      }
  }

Что может улучшить производительность, так это объявить представления памяти непрерывными (т. Е. double[::1] вместо double[:]. В результате код C для a[:]=0.0 будет тогда:

  {
      double __pyx_temp_scalar = 0.0;
      {
          Py_ssize_t __pyx_temp_extent = __pyx_v_a.shape[0];
          Py_ssize_t __pyx_temp_idx;
          double *__pyx_temp_pointer = (double *) __pyx_v_a.data;
          for (__pyx_temp_idx = 0; __pyx_temp_idx < __pyx_temp_extent; __pyx_temp_idx++) {
            *((double *) __pyx_temp_pointer) = __pyx_temp_scalar;
            __pyx_temp_pointer += 1;
          }
      }
  }

Как видно, strides[0] больше не используется в непрерывной версии - strides[0]=1 оценивается во время компиляции, и полученный C-код может быть лучше оптимизирован (см., Например, здесь ) .


Можно испытать желание стать умным и использовать низкоуровневую memset -функцию:

from libc.string cimport memset
memset(&cpc_x[0], 0, 16*sizeof(double))

Однако, для больших массивов не будет никакой разницы по сравнению с использованием непрерывного просмотра памяти (то есть double[::1], см. здесь , например). Для меньших размеров может быть меньше накладных расходов, но я никогда не заботился о том, чтобы проверить.

...