类似于Cython Memoryview as return value,但除了破解生成的C代码外,我没有看到其他解决方案.

我使用的是Cython3.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获取的Memory view初始化代码,_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;
}

我本以为内存视图上的切片会创建一个新的 struct .如果有必要,我可以接受 struct 被初始化,但我真的不希望获得GIL.有没有办法在没有GIL的情况下实现返回的记忆观看?如果我需要初始化一些东西,我如何才能在不复制可能很大的Numpy数组的情况下做到这一点(我只是在读取它).

Edit:我把这个例子做得更小了:

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

cdef float[:] get_upper_left_corner(float[:] arr) noexcept nogil:
    return arr[:2]

推荐答案

如果必须对 struct 进行初始化,我可以接受它,但我真的不希望获得GIL

仅在错误路径上获取GIL(即,如果您try 获取无效切片).如果您查看生成的C代码,您会发现获取GIL的所有时间都由以下人员守卫:

if (unlikely(!__pyx_r.memview))

或者就在标签后面

  __pyx_L1_error:;

Successfully slicing a memoryview does not use the GIL.

对内存视图进行切片需要进行一位引用计数.这是原子的,所以不需要GIL,但也不是非常便宜.所以,最好还是不要把它放在你的内循环中.


一些可能应该出现在原始答案中的细节:

really优化的内存视图访问位是对单个数组元素的访问,而不是切片.实际的切片是使用实用程序代码函数__pyx_memoryview_slice_memviewslice完成的,该函数实际上并不依赖于您设置的Cython指令.

在设置了boundscheck/wraparound指令的情况下,C代码看起来并没有什么不同.仔细想想,这其中有一些是有道理的--bonscheck的主要目的是避免查找内存视图的大小,但在这里您无法避免它,因为它需要设置结果内存视图的大小.

Python相关问答推荐

' osmnx.shortest_track '返回有效源 node 和目标 node 的'无'

Pandas—在数据透视表中占总数的百分比

无法在Docker内部运行Python的Matlab SDK模块,但本地没有问题

改进大型数据集的框架性能

在嵌套span下的span中擦除信息

使用Python查找、替换和调整PDF中的图像'

dask无groupby(ddf. agg([min,max])?''''

Python避免mypy在相互引用中从另一个类重定义类时失败

使用字典或列表的值组合

从源代码显示不同的输出(机器学习)(Python)

如何根据rame中的列值分别分组值

将CSS链接到HTML文件的问题

Python协议不兼容警告

如何获取包含`try`外部堆栈的`__traceback__`属性的异常

以极轴表示的行数表达式?

如何在PYTHON中向单元测试S Side_Effect发送额外参数?

try 使用RegEx解析由标识多行文本数据的3行头组成的日志(log)文件

Pandas:根据相邻行之间的差异过滤数据帧

为什么这个正则表达式没有捕获最后一次输入?

PyTorch变压器编码器中的填充掩码问题