Python Cython-动态2D C+的记忆视图+;排列 目标:< /强>使用Cython获取2D C++字符数组中的内存视图。
一点背景:Python Cython-动态2D C+的记忆视图+;排列 目标:< /强>使用Cython获取2D C++字符数组中的内存视图。,python,cython,typed-memory-views,pep3118,Python,Cython,Typed Memory Views,Pep3118,一点背景: 我有一个本地C++库,它生成一些数据并通过 char ** /代码>返回到Cython世界。阵列在库中初始化和操作如下: struct Result\u缓冲区{ 字符**数据\指针; 整数长度=0; 结果缓冲区(整数行容量){ 数据指针=新字符*[行容量]; 返回arr; } //实际数据被逐行追加 无效附加行(字符*行数据){ 数据指针[长度]=行数据; 长度++; } } 所以我们基本上得到一个嵌套子数组的数组 旁注: -每行的列数相同 -行可以共享内存,即指向同一行
我有一个本地C++库,它生成一些数据并通过<代码> char ** /代码>返回到Cython世界。阵列在库中初始化和操作如下:
struct Result\u缓冲区{
字符**数据\指针;
整数长度=0;
结果缓冲区(整数行容量){
数据指针=新字符*[行容量];
返回arr;
}
//实际数据被逐行追加
无效附加行(字符*行数据){
数据指针[长度]=行数据;
长度++;
}
}
所以我们基本上得到一个嵌套子数组的数组
旁注:-每行的列数相同
-行可以共享内存,即指向同一行数据 目标是更好地将此阵列与memoryview一起使用,而无需昂贵的内存复制
第一次进近(不工作): 使用Cython数组和内存视图: 下面是.pyx文件,它应该使用生成的数据 来自cython cimport视图的
cimport numpy作为np
将numpy作为np导入
[...]
def原始数据到numpy(自):
#源阵列的维度
cdef int ROWS=self.\u row\u count
cdef int COLS=自列计数
这是C++库中的数组,是由CealTyBuffor()创建的
cdef char**raw\U data\U pointer=self.\U raw\U data
#它仅适用于指向第一个嵌套数组的指针
cdef char*指向0的指针=原始数据指针[0]
#现在创建一个2D Cython阵列
cdef view.array cy\U array=指向0的指针
#有了这些,我们最终可以创建NumPy阵列:
返回np.asarray(cy_数组)
这实际上是编译良好,运行时不会崩溃,但结果并不是我所期望的。如果我打印出NumPy数组的值,我会得到以下结果:
000: [1, 2, 3, 4, 5, 6, 7, 8, 9]
001: [1, 0, 0, 0, 0, 0, 0, 113, 6]
002: [32, 32, 32, 32, 96, 96, 91, 91, 97]
[...]
结果表明,第一行映射正确,但其他行看起来更像未初始化的内存。因此,可能与char**
的内存布局和2D MemoryView的默认模式不匹配
编辑#1:我从中了解到,内置cython阵列不支持间接内存布局,因此我必须为
无符号字符**
创建cython包装,它公开了解决方案:
手动执行缓冲协议:
包装类,用于包装无符号字符**
,并实现缓冲区协议(Indirect2DArray.pyx):
产生以下输出:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 4, 5, 6, 7, 8, 9, 1, 2, 3, 7, 8, 9, 1, 2, 3, 4, 5, 6, 2, 1, 4]
[2, 1, 3, 4, 5, 6, 7, 8, 9, 4, 5, 6, 7, 8, 9, 1, 2, 3, 7, 8, 9, 1, 2, 3, 4, 5, 6, 1, 2, 4]
[3, 1, 2, 4, 5, 6, 7, 8, 9, 4, 5, 6, 7, 8, 9, 1, 2, 3, 7, 8, 9, 1, 2, 3, 4, 5, 6, 1, 2, 3]
这正是我所期望的。感谢所有帮助我的人我认为你不能直接从
char**
进入记忆视图。我认为您需要创建一个cdef类,该类封装char**
,然后从中创建memoryview。如果行的维度可能不同,则类型char**
才有意义(性能和清晰度方面),但内存视图不是正确的工具。如果所有行都有相同的维度,那么返回连续内存作为char*
(n倍m长)就更好了-在这种情况下,我会更改接口。好的,我需要更准确地说明源arrray是如何创建的:(我编辑了原始帖子)。此处使用char**
的原因是多行(按第一维度索引)可以共享内存,即指向同一第二维度数组。因此,将连续内存返回为“char*”将扼杀“行共享”的好处。我认为,如果您执行cdef unsigned char[::view.indirect_continuous,::1]arr=wrapper
,它应该会像预期的那样工作。此外,最好将您的编辑提取为自我回答-现在的问题(+答案)太长了。
def test_wrapper(self):
cdef nrows= 10000
cdef ncols = 81
cdef unsigned char** raw_pointer = self.raw_data
wrapper = Indirect2DArray(nrows,ncols)
wrapper.set_raw_data(raw_pointer)
# now create the memoryview:
cdef unsigned char[::view.indirect_contiguous, ::1] view = wrapper
# print some slices
print(list(view[0,0:30]))
print(list(view[1,0:30]))
print(list(view[2,0:30]))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 4, 5, 6, 7, 8, 9, 1, 2, 3, 7, 8, 9, 1, 2, 3, 4, 5, 6, 2, 1, 4]
[2, 1, 3, 4, 5, 6, 7, 8, 9, 4, 5, 6, 7, 8, 9, 1, 2, 3, 7, 8, 9, 1, 2, 3, 4, 5, 6, 1, 2, 4]
[3, 1, 2, 4, 5, 6, 7, 8, 9, 4, 5, 6, 7, 8, 9, 1, 2, 3, 7, 8, 9, 1, 2, 3, 4, 5, 6, 1, 2, 3]