如何避免PyObject_to_MemoryviewSlice,GOTREF / DECREF Python API调用?

时间:2016-05-25 15:21:10

标签: python cython memoryview

我的代码是cython化的问题,更具体地说是以下(和类似的)代码片段:

cdef double [:,:] grad_d_him_d_jm
grad_d_ihm_d_jm = grad_d_im_d_jm(...)

其中grad_d_im_d_jm(...)将返回double [:,:] memoryview。 此代码将由Cython转换为以下C代码:

__pyx_t_1 = __pyx_f_24gradient_better_c_mviews_grad_d_im_d_jm(__pyx_v_i, __pyx_v_j, __pyx_v_m, __pyx_v_structure, __pyx_v_distances); 
if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 203;  __pyx_clineno = __LINE__; goto __pyx_L1_error;}
__Pyx_GOTREF(__pyx_t_1);
__pyx_t_7 = __Pyx_PyObject_to_MemoryviewSlice_dsds_double(__pyx_t_1);
if (unlikely(!__pyx_t_7.memview)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 203; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
__Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
__pyx_v_grad_d_ihm_d_jm = __pyx_t_7;
__pyx_t_7.memview = NULL;
__pyx_t_7.data = NULL;

当我在循环中执行此操作时,我怀疑Python API调用会对我的代码速度产生相当大的影响。

在其他场合也会发生GOTREF / DECREF调用,以及PyFloat_asFloat:

cdef float sp
sp = scalar_product()

其中scalar_product()返回一个cdef float。此代码段已转换为

__pyx_t_1 = __pyx_f_24gradient_better_c_mviews_scalar_product(__pyx_v_i, __pyx_v_j, __pyx_v_m, __pyx_v_structure); 
if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 178; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
__Pyx_GOTREF(__pyx_t_1);
__pyx_t_2 = __pyx_PyFloat_AsFloat(__pyx_t_1); 
if (unlikely((__pyx_t_2 == (float)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 178; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
__Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
__pyx_v_sp = __pyx_t_2;

我正在运行Python 2.7.11+和Cython 0.23.4。如果您能告诉我a)这与性能无关或b)如何解决,我将非常感激。 如果我能改进这个问题,请告诉我,我很乐意这样做。

1 个答案:

答案 0 :(得分:1)

这些似乎是Cython引用计数API的一部分,解释为Maven

我的猜测是grad_d_im_d_jm返回一个Python对象(例如NumPy数组),因此Cython必须在获得内存视图后递减对象引用计数器。

对于scalar_product,我认为它是def(而不是cdef)或者是无类型的。例如以下

cdef g():
    return 1.0

编译到

// ...
__Pyx_XDECREF(__pyx_r);
__Pyx_INCREF(__pyx_float_1_0);
__pyx_r = __pyx_float_1_0;
goto __pyx_L0;

但是,一旦指定了返回类型,引用计数就会消失

cdef float g():
    return 1.0

变为

// ...
__pyx_r = 1.0;
goto __pyx_L0;