我对Python相对较新,这是我第一次尝试编写C扩展。
背景在我的Python 3.X项目中,我需要加载和解析大型二进制文件(10-100MB)以提取数据以进行进一步处理。二进制内容以框架进行组织:标题,然后是可变数量的数据。由于Python的性能较低,我决定去加速c延长加载部分。
独立的C代码在20x-500X之间的一个因素优于Python,因此我对此感到非常满意。
问题:当我在同一python模块中多次调用c延伸的功能时,内存一直在增长。
my_c_ext.c
#include <Python.h>
#include <numpy/arrayobject.h>
#include "my_c_ext.h"
static unsigned short *X, *Y;
static PyObject* c_load(PyObject* self, PyObject* args)
{
char *filename;
if(!PyArg_ParseTuple(args, "s", &filename))
return NULL;
PyObject *PyX, *PyY;
__load(filename);
npy_intp dims[1] = {n_events};
PyX = PyArray_SimpleNewFromData(1, dims, NPY_UINT16, X);
PyArray_ENABLEFLAGS((PyArrayObject*)PyX, NPY_ARRAY_OWNDATA);
PyY = PyArray_SimpleNewFromData(1, dims, NPY_UINT16, Y);
PyArray_ENABLEFLAGS((PyArrayObject*)PyY, NPY_ARRAY_OWNDATA);
PyObject *xy = Py_BuildValue("NN", PyX, PyY);
return xy;
}
...
//More Python C-extension boilerplate (methods, etc..)
...
void __load(char *) {
// open file, extract frame header and compute new_size
X = realloc(X, new_size * sizeof(*X));
Y = realloc(Y, new_size * sizeof(*Y));
X[i] = ...
Y[i] = ...
return;
}
test.py
import my_c_ext as ce
binary_files = ['file1.bin',...,'fileN.bin']
for f in binary_files:
x,y = ce.c_load(f)
del x,y
在这里,我正在删除返回的对象,以期降低内存使用情况。
阅读了几篇文章(例如,这个和这个),我仍然被卡住了。
我尝试添加/删除PyArray_ENABLEFLAGS
设置NPY_ARRAY_OWNDATA
标志,而不会遇到任何区别。我是否尚不清楚NPY_ARRAY_OWNDATA
是否意味着C中的free(X)
。如果我明确释放C中的数组,则在尝试将第二个文件加载到test.py
中的For for循环中时遇到了segfault
。
我对我做错了什么的想法?
这看起来像是内存管理灾难。NPY_ARRAY_OWNDATA
应该使其在数据上调用free
(或至少PyArray_free
不一定是同一件事...)。
然而,一旦完成,您 stly 的全局变量X
和Y
指向现在的存储器区域。然后,您在这些无效的指针上致电realloc
。在这一点上,您对不确定的行为进行了良好的影响,因此任何事情都可能发生。
如果它是一个全局变量,则需要在全球管理内存,而不是由numpy管理。如果内存是由Numpy数组管理的,则需要确保除了通过Numpy数组之外,没有其他方式访问它。其他任何事情都会引起您的问题。