【问题标题】:Passing multidimensional memoryviews to c-function将多维内存视图传递给 c 函数
【发布时间】:2016-01-04 14:06:36
【问题描述】:

由于我有多维数组Passing memoryview to C function,因此以下线程中的答案对我没有帮助。

我有以下测试代码:

头文件:

//cm.h
double per(double **marr,int rows,int cols);

带有 C 函数的文件

//cm.c
#include <stdio.h>
double per(double **marr,int rows,int cols){
    int i,j;
    for (i=0;i<rows;i++){
        for (j=0;j<cols;j++){
            //Just update the array
            marr[i][j] = (double)(i+1)*(j+1);
        }
    }
}

Cython 文件:

#m.pyz
import numpy as np
cimport numpy as np
from cython.view cimport array as cvarray

cdef extern from 'cm.h':
    double per(double **marr,int rows,int cols);

def test(double[:,::1] x):
    cdef int rows = x.shape[0]
    cdef int cols = x.shape[1]
    per(x,rows,cols)
    for i in range(rows):
        for j in range(cols):
            print x[i,j]

以及错误信息:

Error compiling Cython file:
------------------------------------------------------------
...
    double per(double **marr,int rows,int cols);

def test(double[:,::1] x):
    cdef int rows = x.shape[0]
    cdef int cols = x.shape[1]
    per(x,rows,cols)
        ^
------------------------------------------------------------

m.pyx:12:9: Cannot assign type 'double[:, ::1]' to 'double **'

我读过类型化的 memoryviews 是在 Cython 中处理 Python 数组的最现代的方法,但我不知道如何做到这一点。我在 C 中有一些数值配方,它们在动态制作的大型多维数组上运行。 我尝试做的事情是完全错误的吗?

【问题讨论】:

    标签: multidimensional-array cython typed


    【解决方案1】:

    在内部,memoryview 实际上存储为一维数组以及有关维度大小的一些信息。见http://docs.cython.org/src/userguide/memoryviews.html#brief-recap-on-c-fortran-and-strided-memory-layouts

    (作为一个小注解,您可以拥有具有“间接”维度的内存视图,它们确实将事物存储为指向指针的指针。这仅在它们是已经分配了类似内存的事物的视图时才有意义 - 例如,如果你在 C 中构建一个像这样的二维数组。你不会从(比如说)numpy 对象中得到那些,所以我会忽略这个细节)。

    你把C改成

    // pass a 1D array, and then calculate where we should be looking in it
    // based on the stride
    double per(double *marr,int rows,int cols, int row_stride, int col_stride){
        int i,j;
        for (i=0;i<rows;i++){
            for (j=0;j<cols;j++){
                //Just update the array
                marr[row_stride*i + col_stride*j] = (double)(i+1)(j+1);
            }
        }
    }
    

    然后,Cython 代码需要更改以将步幅(以字节存储,因此除以项目大小以获得 C 期望的“双精度数”中的步幅)以及第一个元素的地址

    // also update the cdef defintion for per...
    per(&x[0,0],x.shape[0],x.shape[1],
         x.strides[0]/x.itemsize,x.strides[1]/x.itemsize)
    

    【讨论】:

    • 我需要使用步幅吗?像这样使用指针算法是不正确的:marr[irow +j] 来访问元素。当使用 double *marr = malloc(rowscols*sizeof(double) 制作二维数组时,这可以在纯 c 中找到
    • 也许吧。大步通常有效。您可以获取 numpy 数组的切片(例如array[2:5,3:10]),在这种情况下,步幅将不等于列数。通过将您的数组视图指定为[:,::1],您确保了col_stride=1,但我认为您没有确保row_stride=cols。我决定为您提供始终有效的解决方案,而不必考虑这个问题!
    • 当 row_strides != cols 是因为数组在内存中不连续,即每行之间有“东西”吗?
    • 另一件事。对于 numpy 数组,colum stride = 8 个字节。在 C 中使用它不会得到正确的结果。
    • 1) 是的,原则上每行之间可以有一些东西。它通常是您选择不查看的行的末尾。 2)是我的错误 - 对不起!步幅显然总是以字节为单位,因此您需要除以项目大小。我会编辑我的答案来解决这个问题...
    猜你喜欢
    • 1970-01-01
    • 2014-01-15
    • 1970-01-01
    • 2014-11-19
    • 2014-03-16
    • 2011-09-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多