You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在无GIL环境下用Cython手动创建Memoryview实现多视图访问

使用Cython Memoryview在无GIL环境下实现多视图访问numpy缓冲区

问题背景

我有一个作为缓冲区使用的连续一维numpy数组,通过多个numpy视图以不同形状、维度、偏移量访问数据,Python示例代码如下:

import numpy as np
import math as m

shape_0 =(2,3) 
shape_1 = (2,2,2)

shifts = np.zeros((3),dtype=np.intp)
shifts[1] = m.prod(shape_0)
shifts[2] = shifts[1] + m.prod(shape_1)

buf = np.random.random((shifts[2]))
arr_0 = buf[shifts[0]:shifts[1]].reshape(shape_0)
arr_1 = buf[shifts[1]:shifts[2]].reshape(shape_1)

print(buf)
print(arr_0)
print(arr_1)

需要实现的是:在无GIL环境下,用Cython的Memoryview完成相同功能。


实现方案

Cython Memoryview可以直接映射numpy数组的缓冲区,支持指定偏移、形状和步幅,且能在释放GIL的代码块中操作,完全匹配需求。以下是具体实现步骤:

1. 编写Cython核心代码(buffer_views.pyx)

import numpy as np
cimport numpy as np
from cython cimport nogil
from libc.stdlib cimport malloc, free
from libc.math cimport prod

# 对应numpy float64类型的内存视图类型定义
ctypedef np.float64_t DTYPE_t

def create_and_operate_views(np.ndarray[DTYPE_t, ndim=1] buf):
    cdef:
        # 定义各视图的形状
        int shape_0[2] = [2, 3]
        int shape_1[3] = [2, 2, 2]
        # 计算各段数据的元素偏移量
        Py_ssize_t shift_1 = prod(shape_0, 2)
        Py_ssize_t shift_2 = shift_1 + prod(shape_1, 3)
        
        # 基于原缓冲区创建不同形状的内存视图,::1表示连续内存步幅
        DTYPE_t[:, ::1] arr_0_view = buf[0:shift_1].reshape(shape_0)
        DTYPE_t[:, :, ::1] arr_1_view = buf[shift_1:shift_2].reshape(shape_1)
    
    # 释放GIL执行操作,此区域不受Python全局锁限制
    with nogil:
        # 示例:遍历打印arr_0_view内容
        print("arr_0_view (无GIL环境操作):")
        for i in range(shape_0[0]):
            for j in range(shape_0[1]):
                print(arr_0_view[i][j], end=" ")
            print()
        
        # 示例:遍历打印arr_1_view内容
        print("\narr_1_view (无GIL环境操作):")
        for i in range(shape_1[0]):
            for j in range(shape_1[1]):
                for k in range(shape_1[2]):
                    print(arr_1_view[i][j][k], end=" ")
                print()
            print()
    
    # 可选:将内存视图转回numpy数组,方便Python层验证
    return np.asarray(arr_0_view), np.asarray(arr_1_view)

2. 编写编译脚本(setup.py)

from setuptools import setup
from Cython.Build import cythonize
import numpy as np

setup(
    ext_modules=cythonize("buffer_views.pyx"),
    include_dirs=[np.get_include()]
)

3. 编译与测试

执行编译命令生成扩展模块:

python setup.py build_ext --inplace

在Python中测试功能:

import numpy as np
import math as m
import buffer_views

# 生成与原示例一致的缓冲区数组
shape_0 =(2,3) 
shape_1 = (2,2,2)
shifts = np.zeros((3),dtype=np.intp)
shifts[1] = m.prod(shape_0)
shifts[2] = shifts[1] + m.prod(shape_1)
buf = np.random.random((shifts[2]))

# 调用Cython函数创建视图并执行无GIL操作
cy_arr_0, cy_arr_1 = buffer_views.create_and_operate_views(buf)

# 验证结果与原Python代码一致
print("\n原Python代码生成的arr_0:")
print(buf[0:shifts[1]].reshape(shape_0))
print("\nCython返回的arr_0:")
print(cy_arr_0)

关键说明

  • 无GIL特性:通过with nogil:代码块释放GIL,此区域内的内存视图操作完全不受Python全局解释器锁限制,适合并行计算场景。
  • 零拷贝视图:Memoryview直接映射原numpy数组的缓冲区,没有数据复制,内存效率与numpy原生视图一致。
  • 连续内存适配:::1指定步幅为单个元素大小,匹配原一维连续缓冲区的内存布局,若处理非连续数组可调整步幅参数。

内容的提问来源于stack exchange,提问作者G. Fougeron

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 19:17:04