是否存在将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中是否有更优雅/更快的方法。
答案 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)。较小尺寸的开销可能较少,但我从不关心检查。