Cython:创建返回数组的C函数

时间:2017-10-11 18:55:23

标签: python numpy cython

我想创建一个读取数组并返回数组的Cython函数。这个函数将从其他cdef函数中调用,而不是python def函数。这就是我所拥有的。

在我的.pxd文件中:

cdef int[:] array_test(double *x) nogil

在我的.pyx文件中:

cdef inline int[:] array_test(double *x) nogil:

    cdef int output[2]
    output[0]=1
    output[1]=9

    return output

但是当我编译时,我得到了错误:"没有gil的操作不允许" 有人可以帮忙吗?

1 个答案:

答案 0 :(得分:5)

可能存在误解:此函数不返回c数组而是返回内存视图切片。您不必相信我,您可以通过删除nogil并调用cython来检查它。在创建的* .c文件中,您可以看到函数的C签名,__Pyx_memviewslice是重要的部分:

 static CYTHON_INLINE __Pyx_memviewslice __pyx_f_4file_array_test(CYTHON_UNUSED double *__pyx_v_x)

这个内存视图是一个Python对象,所以它必须更新它的引用计数器(至少在创建时),因此需要全局解释器锁(否则另一个线程可能会弄乱这个非常的计数器而且对象不会在它仍在某处使用时被销毁或销毁) - 这就是你看到“不允许没有gil的操作” - 错误信息的原因。

所以你至少有三个选择:

  1. 用“nogil”做这件事真的很重要吗?如果没有,那就放弃它。这是最简单的解决方案,也是缺点:您可能会失去性能。
  2. 使用真正的C阵列,即int *res = (int *) malloc(2*sizeof(int))。这是快速的,缺点:你必须自己管理内存。
  3. 使用c ++和std::vector<int>,优点是,您不必再管理内存,但需要切换到c ++。
  4. 可能性的改进1.是仅在需要的最后一行获取gil(这对于这个例子没有太大区别,但可能对于实际代码而言):

    cdef inline int[:] array_test(double *x) nogil:
        cdef int output[2]
        output[0]=1
        output[1]=9
        with gil:
            return output
    

    正如OP提出的那样,另一种可能性是将函数的签名更改为:

    cdef inline void array_test(double *x, int[:] output) nogil:
    

    这里的技巧:函数array_test不再创建结果内存视图切片,因此不必进行引用计数 - 这使得“nogil”成为可能。顺便说一下,这是唯一可能的,因为对于'cdef -functions Cython doesn't call Py_INCREF / Py_DECREF for arguments (which it has to do for def`-functions)。

    有一些小的缺点,比如调用array_test变得更麻烦,调用者必须有gil才能创建output内存视图。但它的优点还在于,调用者可以确定应该存储结果的数据结构(numpy数组或其他内容)。

相关问题