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

5
我希望创建一个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个回答

7

可能有一些误解:这个函数并不返回一个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 ,优点是您不再需要管理内存,但您需要切换到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

正如OP所建议的那样,进一步的可能性是改变该函数的签名为:
cdef inline void array_test(double *x, int[:] output) nogil:

这里的诀窍在于函数array_test不再创建结果内存视图,并且不必进行引用计数,这使得“nogil”成为可能。顺便说一下,这是因为对于cdef函数,Cython不会为参数调用Py_INCREF/Py_DECREF(而对于def函数则必须这样做)。
有一些小缺点,比如调用array_test变得更加繁琐,调用者必须拥有GIL才能创建output内存视图。但它也有一个优点,即调用者可以确定结果应该存储在哪种数据结构中(numpy数组或其他某种类型)。

感谢列出这些选项!另一种避开问题的方法是将函数设为void,并让它修改一个额外的空输入数组。 - user3433489

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接