通过Cython将numpy数组传递给C++方法并返回

27

在这个网站上有很多关于在cython中使用numpy的问题,其中一个特别有用的问题是Simple wrapping of C code with cython

然而,cython/numpy接口api 似乎已经有所改变,尤其是确保传递内存连续数组方面。

编写一个在cython中使用以下方式包装函数的最佳方法:

  • 获取一个numpy数组,该数组可能是但不一定是连续的
  • 调用具有签名double* data_in, double* data_out的C ++类方法
  • 返回方法写入的double*的numpy数组?

我的尝试如下:

cimport numpy as np
import numpy as np # as suggested by jorgeca

cdef extern from "myclass.h":
    cdef cppclass MyClass:
        MyClass() except +
        void run(double* X, int N, int D, double* Y)

def run(np.ndarray[np.double_t, ndim=2] X):
    cdef int N, D
    N = X.shape[0]
    D = X.shape[1]

    cdef np.ndarray[np.double_t, ndim=1, mode="c"] X_c
    X_c = np.ascontiguousarray(X, dtype=np.double)

    cdef np.ndarray[np.double_t, ndim=1, mode="c"] Y_c
    Y_c = np.ascontiguousarray(np.zeros((N*D,)), dtype=np.double)

    cdef MyClass myclass
    myclass = MyClass()
    myclass.run(<double*> X_c.data, N, D, <double*> Y_c.data)

    return Y_c.reshape(N, 2)

这段代码可以编译,但不一定是最优的。你有没有对上面代码片段进行改进的建议?

同时,当在运行时调用该代码时,会抛出并显示“np未定义于第X_c行”,请注意(2)。具体的测试代码和错误信息如下:

import numpy as np
import mywrapper
mywrapper.run(np.array([[1,2],[3,4]], dtype=np.double))

# NameError: name 'np' is not defined [at mywrapper.pyx":X_c = ...]
# fixed!

5
在您的.pyx文件中仍需导入import numpy as np才能使用NumPy函数(cimport numpy as np用于导入有关NumPy模块的特殊编译时信息)。 - jorgeca
@jorgeca 我猜你的评论回答了OP的问题... - Saullo G. P. Castro
1
@SaulloCastro 我把它发布为评论,因为我认为这只是一个小障碍,但我不知道编写这些接口的最佳方法是什么。 - jorgeca
@jorgeca 谢谢,确实是缺少语句导致了错误信息。你说得对,我主要是在寻找优化方案 :-) - Michael Schubert
1个回答

20

你的理解基本正确。首先,优化应该不是一个大问题。理想情况下,大部分时间都花费在C++内核中,而不是cython包装代码中。

有一些风格上的改变可以使你的代码更简单。 (1) 在 1D 和 2D 数组之间进行重塑是不必要的。当你知道数据的内存布局 (C-order vs. fortran order, striding 等)时,你可以将数组看作一个你将在 C++ 中自行索引的内存块,所以numpy的 ndim 在 C++方面并不重要 -- 它只是看到那个指针。(2) 使用 cython 的地址运算符 &,你可以以更简洁的方式获得指向数组开头的指针 -- 不需要显式转换 -- 使用 &X[0,0]

这是我编辑后的原始片段:

cimport numpy as np
import numpy as np

cdef extern from "myclass.h":
    cdef cppclass MyClass:
        MyClass() except +
        void run(double* X, int N, int D, double* Y)

def run(np.ndarray[np.double_t, ndim=2] X):
    X = np.ascontiguousarray(X)
    cdef np.ndarray[np.double_t, ndim=2, mode="c"] Y = np.zeros_like(X)

    cdef MyClass myclass
    myclass = MyClass()
    myclass.run(&X[0,0], X.shape[0], X.shape[1], &Y[0,0])

    return Y

1
请问,使用Cython中的类型化内存视图而不是传递数组可以完成这个任务吗?我不确定这样做是否会节省一些内存开销等。 - krishnab

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接