将cython cdef扩展数组设置为零

时间:2018-04-29 21:32:58

标签: python cython

是否存在将cdef数组设置为零的cython-ic方式。我有一个带有以下签名的函数:

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 :(得分:1)

我假设您已经在使用@cython.boundscheck(False),因此您无法在性能方面做出改进。

出于可读性的原因,我会使用:

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

cython会将此转换为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[:]a[:]=0.0生成的C代码将是:

  {
      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代码(例如参见here })。

人们可能会想要聪明并使用低级memset - 函数:

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

但是,对于较大的数组,与连续内存视图的使用相比没有区别(即double[::1],例如见here)。较小尺寸的开销可能较少,但我从不关心检查。