Cython 返回的内存视图始终被视为未初始化

问题描述 投票:0回答:1

类似于 Cython Memoryview 作为返回值,但除了破解生成的 C 代码之外,我没有看到其他解决方案。

我使用的是Cython 3.0,但看起来结果和

<3
是一样的。

这是一个例子:

# cython: language_level=3, boundscheck=False, cdivision=True, wraparound=False, initializedcheck=False, nonecheck=False
cimport cython
from cython cimport floating
import numpy as np
cimport numpy as np

np.import_array()

def test_func():
    cdef np.ndarray[float, ndim=2] arr = np.zeros((5, 5), dtype=np.float32)
    cdef float[:, ::1] arr_view = arr
    _run(arr_view)

cdef void _run(floating[:, ::1] arr_view) noexcept nogil:
    cdef floating[:, :] tmp = _get_upper_left_corner(arr_view)

cdef inline floating[:, :] _get_upper_left_corner(floating[:, ::1] arr) noexcept nogil:
    return arr[:-1, :-1]

然后运行

cython -a cython_test.pyx
,它显示
_get_upper_left_corner
函数具有包含 GIL 获取的内存视图初始化代码,并且
_run
函数具有错误检查,因为
_get_upper_left_corner
函数可能会返回错误(至少这是我的猜测):

+17: cdef inline floating[:, :] _get_upper_left_corner(floating[:, ::1] arr) noexcept nogil:

static CYTHON_INLINE __Pyx_memviewslice __pyx_fuse_0__pyx_f_11cython_test__get_upper_left_corner(__Pyx_memviewslice __pyx_v_arr) {
  __Pyx_memviewslice __pyx_r = { 0, 0, { 0 }, { 0 }, { 0 } };
/* … */
  /* function exit code */
  __pyx_L1_error:;
  #ifdef WITH_THREAD
  __pyx_gilstate_save = __Pyx_PyGILState_Ensure();
  #endif
  __PYX_XCLEAR_MEMVIEW(&__pyx_t_1, 1);
  __pyx_r.data = NULL;
  __pyx_r.memview = NULL;
  __Pyx_AddTraceback("cython_test._get_upper_left_corner", __pyx_clineno, __pyx_lineno, __pyx_filename);
  goto __pyx_L2;
  __pyx_L0:;
  if (unlikely(!__pyx_r.memview)) {
    #ifdef WITH_THREAD
    PyGILState_STATE __pyx_gilstate_save = __Pyx_PyGILState_Ensure();
    #endif
    PyErr_SetString(PyExc_TypeError, "Memoryview return value is not initialized");
    #ifdef WITH_THREAD
    __Pyx_PyGILState_Release(__pyx_gilstate_save);
    #endif
  }
  #ifdef WITH_THREAD
  __Pyx_PyGILState_Release(__pyx_gilstate_save);
  #endif
  __pyx_L2:;
  return __pyx_r;
}


我假设内存视图上的切片会创建一个新的结构。如果必须的话,我可以接受结构体的初始化,但我真的不希望获取 GIL。有没有办法在没有 GIL 的情况下完成返回的内存视图?如果我需要初始化某些东西,我怎样才能在不复制可能很大的 numpy 数组的情况下做到这一点(我只是在读它)。

python numpy cython memoryview
1个回答
0
投票

您在生成的 C 代码中看到的 GIL(全局解释器锁)获取是由于 Cython 在多线程场景中确保内存视图一致性的默认行为。不幸的是,即使您只读取内存视图,这也可能会导致 GIL 获取。如果您确定您的使用不会因多线程而导致冲突,您可以使用

wraparound
指令明确告诉 Cython 您不需要 GIL 来执行这些内存视图操作。

wraparound=False
添加到代码顶部的
cython
指令,如下所示:

# cython: language_level=3, boundscheck=False, cdivision=True, wraparound=False, initializedcheck=False, nonecheck=False

这应该可以防止 Cython 在生成的 C 代码中插入 GIL 获取以进行内存视图切片操作,并且它应该有助于实现在没有 GIL 参与的情况下返回内存视图的目标。只要确保您的代码在多线程上下文中确实可以安全运行而无需 GIL。

请记住,在内存视图操作中禁用 GIL 保护可能需要格外小心,因为您要承担线程安全的责任。确保彻底测试您的代码以确保其行为符合预期。

© www.soinside.com 2019 - 2024. All rights reserved.