Полагаю, вы уже используете @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]
, см. здесь , например). Для меньших размеров может быть меньше накладных расходов, но я никогда не заботился о том, чтобы проверить.