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

use*_*489 5 python numpy cython

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

在我的.pxd文件中:

cdef int[:] array_test(double *x) nogil
Run Code Online (Sandbox Code Playgroud)

在我的.pyx文件中:

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

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

    return output
Run Code Online (Sandbox Code Playgroud)

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

ead*_*ead 7

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

 static CYTHON_INLINE __Pyx_memviewslice __pyx_f_4file_array_test(CYTHON_UNUSED double *__pyx_v_x)
Run Code Online (Sandbox Code Playgroud)

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

所以你至少有三个选择:

  1. 用"nogil"做这件事真的很重要吗?如果没有,那就放弃它.这是最简单的解决方案,也是缺点:您可能会失去性能.
  2. 使用真正的C阵列,即int *res = (int *) malloc(2*sizeof(int)).这是快速的,缺点:你必须自己管理内存.
  3. 使用c ++ std::vector<int>,优点是,您不必再管理内存,但需要切换到c ++.

可能性1.的改进是仅在需要的最后一行获取gil(这对于这个例子没有太大的区别,但可能对于实际代码而言):

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

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

cdef inline void array_test(double *x, int[:] output) nogil:
Run Code Online (Sandbox Code Playgroud)

这里的技巧:功能array_test不再创建结果内存视图切片,因此不必进行引用计数 - 这使得"nogil"成为可能.顺便说一下,这是唯一可能的,因为对于'cdef cdefPy_INCREF/Py_INCREF/Py_DECREFPy_DECREF def`-函数).

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