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

如何使用Cython基于Shape、Strides和Double*指针表示非连续内存视图?

在Cython中用Shape、Strides和指针创建对应NumPy切片的内存视图

好问题!NumPy的切片(比如你例子里的arr_slice和arr_tile)本质上是共享原始数组的数据,只改变了形状(shape)和步长(strides)——而Cython的内存视图完全支持这种“零拷贝”的视图操作,只要你能拿到原始数据的指针、目标视图的形状和步长,就能直接创建对应的内存视图。下面我结合你的示例一步步说明:

先明确你的示例对应的参数值

先把你NumPy代码里的两个切片对应的参数列出来,方便后续对应:

  • 原始数组arr:shape=(10,30),dtype=float64(每个元素8字节),行步长是30*8字节,列步长是8字节
  • arr_slice = arr[::2]:取所有偶数行,所以shape=(5,30),行步长变成30*8*2(每次跳2行),列步长还是8字节,数据指针和原始数组一致
  • arr_tile = arr[:3, :5]:取前3行前5列,shape=(3,5),行步长还是30*8(因为还是按原始行的间隔取),列步长8字节,数据指针指向原始数组的第一个元素

方法1:借助NumPy数组中转(简单直观)

如果你在Cython里已经能拿到原始数组的指针、目标shape和strides,可以先创建一个不拷贝数据的NumPy数组,再转成Cython内存视图:

import numpy as np
cimport numpy as np

# 假设我们已经获取到以下参数(对应arr_slice的情况)
cdef double* data_ptr = <double*>arr.data  # arr是原始NumPy数组
cdef Py_ssize_t shape[2] = [5, 30]
cdef Py_ssize_t strides[2] = [30*8*2, 8]  # float64是8字节,行步长翻倍

# 创建不拷贝数据的NumPy数组
cdef np.ndarray slice_np = np.ndarray(
    shape=shape,
    dtype=np.float64,
    buffer=data_ptr,
    strides=strides
)

# 转成Cython内存视图
cdef double[:, :] slice_view = slice_np

# 测试:修改视图的值,原始数组也会改变(验证零拷贝)
slice_view[0, 0] = 99.9
print(arr[0, 0])  # 会输出99.9,说明共享数据

对应arr_tile的话,只需要修改shape和strides:

cdef Py_ssize_t tile_shape[2] = [3, 5]
cdef Py_ssize_t tile_strides[2] = [30*8, 8]  # 行步长和原始数组一致

cdef np.ndarray tile_np = np.ndarray(
    shape=tile_shape,
    dtype=np.float64,
    buffer=data_ptr,
    strides=tile_strides
)

cdef double[:, :] tile_view = tile_np

方法2:纯Cython方式(不依赖NumPy中转)

如果你不想依赖NumPy的数组构造,可以直接用Cython内置的cython.view.array来创建内存视图,更底层也更高效:

from cython.view cimport array as cython_array
from libc.stddef cimport Py_ssize_t

# 对应arr_slice的情况
cdef double* data_ptr = <double*>arr.data
cdef Py_ssize_t slice_shape[2] = [5, 30]
cdef Py_ssize_t slice_strides[2] = [30*8*2, 8]

# 直接创建内存视图
cdef double[:, :] slice_view = cython_array(
    shape=slice_shape,
    itemsize=sizeof(double),  # float64的字节数
    format="d",  # 格式符,对应double
    strides=slice_strides,
    ptr=data_ptr
)

# 同样验证共享数据
slice_view[1, 1] = 88.8
print(arr[2, 1])  # 输出88.8,因为arr_slice的第1行对应arr的第2行

# 对应arr_tile的情况
cdef Py_ssize_t tile_shape[2] = [3, 5]
cdef Py_ssize_t tile_strides[2] = [30*8, 8]

cdef double[:, :] tile_view = cython_array(
    shape=tile_shape,
    itemsize=sizeof(double),
    format="d",
    strides=tile_strides,
    ptr=data_ptr
)

注意事项

  • 确保strides的单位是字节:NumPy的arr.strides返回的就是字节数,所以直接用就行;如果是自己计算的话,要记得乘以元素的字节长度(比如float64是8)
  • 内存视图的生命周期要和原始数据一致:如果原始数据被释放了,内存视图就会变成野指针,所以要保证原始数组在视图使用期间一直存在
  • 如果你需要更灵活的步长(比如非连续的列),只需要修改对应的strides值就行,原理是一样的

内容的提问来源于stack exchange,提问作者user11488411

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 19:27:52