使用 ctypes 从 C 结构数组到 NumPy 数组的高效转换

Posted

技术标签:

【中文标题】使用 ctypes 从 C 结构数组到 NumPy 数组的高效转换【英文标题】:Efficient conversion from C array of struct to NumPy array with ctypes 【发布时间】:2018-06-12 16:57:46 【问题描述】:

在此question 之后,我尝试将 C++ DLL 与 Cython 一起使用,并提供了一个适合我的案例但从未奏效的教程,我决定使用 ctypes。由于繁重的 SO 浏览,我现在使用 ctypes 成功调用了我对我的 DLL 感兴趣的函数。我现在面临在 Python 中使用结构数组的结果的问题。

这个C函数如下:

void myfun(
                 double         a,
                 //...more double parameters
                 int            max_iter,
                 int *          nb_iter,
                 myStruct *     res_arr,
                 bool *         ok
                );

myStruct 定义如下:

typedef struct  
             double dat;

             int    k;
             int    m;
            // ... more int

             double b;
             double v;
             //...more double

             myStruct;

我通过以下 Python 代码调用这个函数:

import ctypes
lib = ctypes.CDLL('PATH_TO_DLL\\lib.dll')

myFunPy = getattr(lib,"?myFun@@YANNNNN_BUNCH_OF_Ns_NNNHPEAHPEAUmyStruct@@PEA_N@Z") # name found through dumpbin.exe (due to C++)

class myStruct(ctypes.Structure):
    _fields_ = [("k", ctypes.c_int),
                ("m", ctypes.c_int),
                #...more int parameters

                ("b", ctypes.c_double),
                ("v", ctypes.c_double)
                #...more double parameters
               ]

myFunPy.argtypes = [ctypes.c_double,
                   // ... more double parameters

                   ctypes.c_int,
                   ctypes.POINTER(ctypes.c_int),
                   ctypes.POINTER(myStruct),
                   ctypes.POINTER(ctypes.c_bool)]

myFunPy.restype = ctypes.c_void_p

max_iter = 10000
a = ctypes.c_double(0.1)
// ... more double parameters definitions

nb_iter = ctypes.c_int(0) # value doesn't matter, it is initialized in myFun
ok = ctypes.c_bool(True)

res_arr = (myStruct * max_iter)()

myFunPy(a, ..., max_iter, ctypes.byref(nb_iter), res_arr, ctypes.byref(ok))

现在myFun 修改了res_arr,这是一个结构数组,从上面的代码可以看出。 正是

<__main__.myStruct_Array_10000 at 0x97966c8>)

在上面显示的代码之后,但我不明白如何将其转换为 NumPy 数组以供将来使用有效

当然,我可以使用for field, _ in struct._fields_ 之类的东西进行循环,如here 所示,但这不是重点,因为我使用 DLL 来加快计算速度(我真的看到了执行时间的差异)。 res_arr 范围从 200 kb 到 1 Mb,有数万行和几十列,所以我确信有一种方法可以不用循环遍历所有这些,但我想不通如何做得很好。

看来,如果不是struct数组,那就更简单了。有afewSOquestions(还有here、here、here和here和here)接近这个主题,但它要么只是转换一个结构,只是一个数组,或者与我很接近但从未完全像我一样的东西,而且我在调整这些解决方案方面没有成功,所以也许有一种方法可以以此为基础来回答,但无论如何我都在听。

【问题讨论】:

【参考方案1】:

我们遇到了几乎相同的问题,但就我而言,我使用的是 CUDA DLL,所以我的编译器是 nvcc。但我相信这也可以通过普通的g++ 编译器来完成。无论如何,这里是我为了将结构数组从我的 CPP 文件转换为可用的 Python 列表/数组而执行的步骤。我不会浏览你的代码;相反,我只会给你举个例子,可以在这里找到:https://github.com/jcbacong/python-cpp.git

但重要的步骤总结如下:

    使用必要的头文件创建一个 .cpp 文件,其中包含函数的 extern "C" 声明。在我的 .cpp 文件中,我返回了一个结构数组,而不是返回 void

    使用您的编译器创建一个 .dll 文件。同样,就我而言,它是nvcc。我通过我的 github 帐户链接的示例代码是使用 nvcc 编译的。

    在您的 .py 文件中:

    3.1 使用ctypes.Structure 创建一个 Python 类,以便复制 .cpp/.h 文件中的结构定义。

    3.2 使用argtype/restype 初始化您的输入/输出。由于我的 .cpp 函数返回一个结构数组,restypectypes.Pointer(&lt;your Python Class(ctypes.Structure)&gt;) 给出。

    3.3 我将所有输入转换为可读的 ctype。在我的 .py 文件中调用函数后,可以使用 (results = _results[:ARRAY_SIZE]) 将生成的结构数组(示例中为 _results)转换为 Python 列表。

希望对你有帮助!!

【讨论】:

确实有效,谢谢!实际上,我已经重写了我的 C 代码以使用 Double 数组,而忘记了 Struct,因为在我的情况下它可以很容易地完成(我只有 Double 和 Int 在那里)。而且我还意识到,如果我想轻松使用它,我必须输出数组。这是最好的解决方案!

以上是关于使用 ctypes 从 C 结构数组到 NumPy 数组的高效转换的主要内容,如果未能解决你的问题,请参考以下文章

使用 ctypes 从 python 到 c++ 的数据传输

python 和 ctypes 访问具有嵌套结构的 c++ 类

如何使用 ctypes 将数组从 C++ 函数返回到 Python

使用 numpy/ctypes 公开 C 分配的内存缓冲区的更安全方法?

将 C 指针转换为 Python numpy 数组

ctypes给扩展模块中的函数传递数组和结构体