如何在 Cython 中将大型 malloc 数组返回或保存为 Python 对象?

Posted

技术标签:

【中文标题】如何在 Cython 中将大型 malloc 数组返回或保存为 Python 对象?【英文标题】:How to return or save large malloc'd arrays in Cython as Python objects? 【发布时间】:2021-12-11 05:28:49 【问题描述】:

我想使用 Cython 从模型中创建大量模拟样本,稍后我需要使用 Python 对其进行分析。我的模拟脚本一次运行的结果应该是一个 10000 x 10000 的数组。

我使用def 定义了一个函数,并尝试将我的数组声明为cdef int my_array[10000][10000]my_script.pyx 文件编译正确,但是当我运行脚本时出现“分段错误”错误(我在 Linux 上)。

在寻找解决方案时,我了解到这个问题是由于在堆栈而不是堆上分配内存造成的,所以我决定使用PyMem_Malloc 来分配内存。这是我正在尝试做的最低版本:

import cython
from cpython.mem cimport PyMem_Malloc
from libc.stdlib cimport rand, srand, RAND_MAX

srand(time(NULL))

def my_array_func(int a_param)
    cdef int i
    cdef int **my_array = <int **>PyMem_Malloc(sizeof(int *) * 10000)
    for i in range(10000):
        my_array[i] = <int *>PyMem_Malloc(sizeof(int) * 10000)
    
    cdef int j
    cdef int k
    for j in range(10000):
        for k in range(10000):
            my_array[j][k] = <float>rand()/RAND_MAX * a_param
    
    return my_array

当我尝试编译这个文件时,我收到了一个错误Cannot convert 'int **' to Python object,这是有道理的,因为 my_array 不是一个正确的数组,所以我猜它不能作为 Python 对象返回(对不起,我对 C 的了解真的很生疏)。

有没有办法让函数返回我的二维数组,以便它可以用作其他 Python 函数的输入?另一个更受欢迎的解决方案可能是将数组直接保存在一个文件中,以后可以通过 Python 脚本导入。

谢谢。

【问题讨论】:

你说的是什么python对象? numpy.ndarray?其他常见的python类有intlistdict。还有一个通用的object 类,但它没有任何(许多)定义的方法。 数组包含相同类型的数字,因此numpy.ndarraylist 工作得同样好。但是,我发现的唯一解决方案是遍历指针的 my_array 指针并一次将值分配给 python 对象,这让我回到了最初的问题(堆栈上的内存和分段错误错误) . 为什么不直接使用 10000 x 10000 的 numpy 数组而不是 malloced C 数组? 【参考方案1】:

根据@DavidW 的评论,当 Cython 中涉及矩阵计算时,建议使用 numpy 数组来拥有内存并居住在 pythonland 中。

在你的情况下,它看起来像这样:

import cython
cimport numpy as np
import numpy as np
from libc.stdlib cimport rand, srand, RAND_MAX
from libc.time cimport time

srand(time(NULL))

def my_array_func(int a_param):
    cdef int n_rows=10000, ncols=10000
    # Mem alloc + Python object owning memory
    cdef np.ndarray[dtype=int, ndim=2] my_array = np.empty((n_rows,ncols), dtype=int)

    # Memoryview: iterate over my_array at C speed
    cdef int[:,::1] my_array_view = my_array 

    # Fill array
    cdef int i, j
    for i in range(n_rows):
        for j in range(ncols):
            my_array_view[i,j] = <int> (rand()/RAND_MAX * a_param)
    
    return my_array

分配具有定义大小的空内存块,确保它由 Python 对象拥有并具有所有不错的数组属性(如.shape),这就是您在cdef np.ndarray[... 的一行中得到的。使用 memoryview 可以在没有 Python 交互的情况下循环这个数组。

【讨论】:

my_array_view[i][j] 可能会创建一个中间 1D 内存视图 - 你可能会更好(而且绝对不会更糟)my_array_view[i, j] 真的!一个易于纠正的侥幸 谢谢你们。我认为纯 Cython 解决方案(不涉及 Numpy)会更有效,但运行时间似乎是可以接受的。

以上是关于如何在 Cython 中将大型 malloc 数组返回或保存为 Python 对象?的主要内容,如果未能解决你的问题,请参考以下文章

减少cython并行中的数组

如何在Android中将大型json数组拆分为页面

如何在 cython 中调用此函数?

如何在 Windows 8.1 中的 anaconda(python3.6) 中将 cython pyx 构建为 pyd?

C 中大型动态数组分配 (malloc) 期间的内存错误。它适用于较小的数组

malloc 和全局变量声明在 C 中将它们的变量分配到哪里? [复制]