为什么我不能将c数组传递给期望nogil内容中的内存视图的函数?

时间:2015-03-24 17:45:35

标签: cython

cdef double testB(double[:] x) nogil:
    return x[0]

def test():
    cdef double xx[2]
        with nogil:
            testB(xx)

# compiler error: Operation not allowed without gil

如果使用gil,它可以正常工作。

是因为当传入一个c数组时,它会创建一个内存视图,这样的创建动作实际上需要gil吗?那么内存视图不完全是一个c对象吗?

更新

%%cython --annotate
cimport cython

cdef double testA(double[:] x) nogil:
    return x[0]

cpdef myf():
    cdef double pd[8]
    cdef double[:] x = pd
    testA(x)

cdef double[:] x = pd编译为:

  __pyx_t_3 = __pyx_format_from_typeinfo(&__Pyx_TypeInfo_double);
  __pyx_t_2 = Py_BuildValue((char*) "("  __PYX_BUILD_PY_SSIZE_T  ")", ((Py_ssize_t)8));
  if (unlikely(!__pyx_t_3 || !__pyx_t_2 || !PyBytes_AsString(__pyx_t_3))) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 8; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_3);
  __Pyx_GOTREF(__pyx_t_2);
  __pyx_t_1 = __pyx_array_new(__pyx_t_2, sizeof(double), PyBytes_AS_STRING(__pyx_t_3), (char *) "fortran", (char *) __pyx_v_pd);
  if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 8; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
  __pyx_t_4 = __Pyx_PyObject_to_MemoryviewSlice_ds_double(((PyObject *)__pyx_t_1));
  if (unlikely(!__pyx_t_4.memview)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 8; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_DECREF(((PyObject *)__pyx_t_1)); __pyx_t_1 = 0;
  __pyx_v_x = __pyx_t_4;
  __pyx_t_4.memview = NULL;
  __pyx_t_4.data = NULL;

存在__Pyx_PyObject_to_MemoryviewSlice_ds_double。因此,在绑定内存视图时,它确实需要gil。

1 个答案:

答案 0 :(得分:1)

你应该使用一个numpy数组,因为你的cdef double [:]声明被Python对象包装,并且它的使用受到限制而没有gil。您可以尝试切片double[:]

来查看
def test()
    cdef double[:] asd
    with nogil:
        asd[:1]

您的输出将是:

  with nogil:
            asd[:1]
              ^
    ------------------------------------------------------------

prueba.pyx:16:11: Slicing Python object not allowed without gil

使用numpy数组会编译; numpy使用Python缓冲区protocole,并与Cython顺利集成(Google Summercamp项目的资金来源于此)。所以def中没有出现包装冲突:

import numpy as np

    cdef double testA(double[:] x) nogil:
        return x[0]

    cpdef test():
        xx = np.zeros(2, dtype = 'double')
        with nogil:
            a = testB(xx)
        print(a)

这将构建带有test()的模块。但它以一种丑陋的方式崩溃(至少在mi PC上):

Process Python segmentation fault (core dumped)

如果我可以坚持使用我的(现已删除的)上一个答案,根据我自己的经验,在处理Cython内存视图和C数组时,传递指针的工作方式与C中预期的一样。大多数包装都是避免的(实际上,你正在编写完全符合您想要的方向的代码,从而进行不必要的包装。这将按预期编译和运行:

cdef double testB(double* x) nogil:
    return x[0]

def test():
    cdef double asd[2]
    asd[0] = 1
    asd[1] = 2
    with nogil:
        a = testB(asd)
    print(a)

并且,在compilig之后:

In [5]: import prueba

In [6]: prueba.test()
1.0

Memoryviews本身不是Python对象,但它们可以包含在一个中。我不是一个熟练的Cython程序员,所以有时我得到意想不到的包装或代码仍然在Python级别,当我认为它将在C.试用和错误让我到指针策略。