【问题标题】:Return a 2D Cython pointer to Python array返回一个指向 Python 数组的 2D Cython 指针
【发布时间】:2020-09-16 22:30:34
【问题描述】:

我目前正在从 Cython 向 C 传递一个指针的以下指针:

    #convert the input Python 2D array to a memory view
    cdef double[:,:] a_cython= np.asarray(a,order="C")
    #define a pointer of a pointer with dimensions of a
    cdef double** point_to_a = <double **>malloc(N * sizeof(double*))
    #initialize the pointer
    if not point_to_a: raise MemoryError
    #try:
    for i in range(N):
        point_to_a[i] = &a_cython[i, 0]

    #pass this double pointer to a C function
    logistic_sigmoid(&point_to_a[0], N,M)

其中a 是一个numpy 数组,其维度为N x Mpoint_to_a 是指向Cython memoryview a_cython 的指针的Cython 指针。由于来自 Python 的输入 a 是二维数组,我认为这是将信息直接传递给 C 的最佳方法。 段落顺利,计算正确完成。但是,我现在正在尝试将 point_to_a 重新转换回 numpy 数组,但我有点挣扎。

我正在考虑各种解决方案。我想探索是否有可能在整个过程中保留一个 N 维数组,因此我在 Cython 中尝试了这种方法:

    #define a integer array for dimensions
    cdef np.npy_intp dims[2]
    dims[0]=  N
    dims[1] = M
    #create a new memory view and PyArray_SimpleNewFromData to deal with the pointer
    cdef np.ndarray[double, ndim=2] new_a =  np.PyArray_SimpleNewFromData(2, &dims[0], np.NPY_DOUBLE, point_to_a)

但是,当我将 new_a 转换为 np.array 作为 array = np.asarray(new_a) 时,我有一个只有 0 的数组。 你有什么想法吗?

非常感谢

【问题讨论】:

    标签: python c arrays numpy cython


    【解决方案1】:

    一旦您使用int**(或类似名称),您的数据就会处于所谓的间接内存布局中。 Cython 的类型化内存视图支持间接内存布局(参见例如 Cython: understanding a typed memoryview with a indirect_contignuous memory layout),但是实现此接口的类并不多。

    Numpy 的 ndarray 不实现间接内存布局 - 它们仅支持直接内存布局(例如,int* 类型的指针而不是 int**),因此将 int** 传递给 numpy 数组将没有好处。

    好消息是,因为您与a_cython 共享内存,所以值已经就地更新了。您可以通过返回类型化内存视图的base-object 来获取底层的numpy数组,即

    return a_cython.base # returns 2d-numpy array.
    

    根本不需要复制内存!


    但是内存管理存在一些问题(例如,您需要释放 point_to_a)。

    在你的情况下这可能有点过头了,但我利用这个机会无耻地插入了我的库 indirect_buffer:因为间接内存布局缓冲区的替代品很少,有时需要一个,我'我们创建了一个以避免编写总是相同的代码。

    使用indirect_buffer,您的函数可能如下所示:

    %%cython
    #just an example for a c-function
    cdef extern from *:
        """
        void fillit(int** ptr, int N, int M){
           int cnt=0;
           for(int i=0;i<N;i++){
              for(int j=0;j<M;j++){
                ptr[i][j]=cnt++;
              }
           }
        }
        """
        void fillit(int** ptr, int N, int M)
    
    from indirect_buffer.buffer_impl cimport IndirectMemory2D
    def py_fillit(a):
        #create collection, it is a view of a
        indirect_view=IndirectMemory2D.cy_view_from_rows(a, readonly=False)
        fillit(<int**>indirect_view.ptr, indirect_view.shape[0], indirect_view.shape[1])
        # values are updated directly in a
    

    现在可以使用,例如:

    import numpy as np
    a=np.zeros((3,4), dtype=np.int32)
    py_fillit(a)
    print(a)
    # prints as expected:
    #  array([[ 0,  1,  2,  3],
    #         [ 4,  5,  6,  7],
    #         [ 8,  9, 10, 11]])
    

    上面的版本做了很多正确的事情:内存管理、缓冲区锁定等等。

    【讨论】:

    • a_cython._base 我完全忘记了这个!非常感谢您在indirect_buffer repo 中的辛勤工作。绝对是日常使用的超级资源。谢谢一百万!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-01-12
    • 1970-01-01
    • 2017-02-07
    • 1970-01-01
    • 1970-01-01
    • 2014-12-30
    • 1970-01-01
    相关资源
    最近更新 更多