【发布时间】:2020-01-24 17:39:17
【问题描述】:
我正在尝试尽可能高效地将一个双精度数组从 C 库发送到 Python。 与这个库通信的代码是由不同的公司创建的(它包括许多方法、异常等),但是这个特殊的函数会创建一个列表并从 C 中的每个项目中插入一个 Python 对象数组,如果你关心速度,这是非常低效的。
这是编译后用于创建 python 模块的 C 代码的 sn-p:
static PyObject* foo(PyObject* self, PyObject* args) {
double *val = 0;
//more variables
//Note that this uses the Python C API PyArg_ParseTuple to handle the parameters
if (! PyArg_ParseTuple(args, "ii", &listID, &size)) {
//send exception
}
//some code here that allocates an array to hold "val" and calls the C library
PyList_New(size);
for(i = 0; i < size; i++) {
PyList_SET_ITEM(retData, i, Py_BuildValue("d", val[i]));
}
//free resources, return the Python object
}
我发现Python array 可能很有用,另外还有一个好处是适合多进程。
如果 Python 数组按我想象的那样工作,我可以在 Python 中分配数组,然后 C 库就填充它
from cpython cimport array
import array
from dalibrary import dafunction
cdef array.array a = array.array('d', [])
array.resize(a, 1000)
dafunction(array, 1000) # In a very "C" style, the array would be filled with values
print(array)
问题在于我没有找到有关使用 Python 数组所需的 C 代码的文档。至少不使用 Python C API。
注意:我知道 ctypes,但这意味着重写整个模块,如果可能的话,我宁愿不这样做(但缺乏文档可能会驱使我去那里)
似乎有人已经问过类似的问题here,但仍未解决
结果:
我设法执行了我想要的(正如您在其中一个答案中看到的那样),甚至对数组(多进程数组)使用多线程,但令我惊讶的是,它实际上比使用所谓的低效(但健壮的)IPC 方法,例如带有 Python 列表的队列。
由于使用 Python API 很困难,而且它给了我零改进,我认为社区的最佳答案是使用 ctypes 的建议。我会保留我的答案以供参考。也许有人发送大量内存可能会从中受益。
【问题讨论】:
-
根据您打算如何处理 Python 端的数据,填充列表很容易成为最有效的选择。
-
数据主要用于从该模块中获取值。在我们的特殊情况下,我们甚至需要将数据传输到不同的 python 进程(长话短说)。列表不能用作共享内存(但直接转换为 C 对象的对象可以)
-
您也不能共享
array.array数组(尽管使用这样的数组传输数据会更容易)。 -
我怀疑
array.array数组实际上可能不是您的最佳选择,但无论您最终使用什么,它都可能提供buffer protocol。这包括如果你使用array.array数组毕竟。您可以使用缓冲区协议从 C 访问数据。(我没有将其发布为答案,因为我不确定缓冲区形状和步幅如何与各种请求类型交互的一些细节。) -
@user2357112supportsMonica 我用你关于缓冲协议的评论来构建一个例子。但如果你想添加一个更简洁的例子,我可以接受你的回答。