使用PyArray_SimpleNewFromData()创建数组并返回时,Python扩展中出现内存泄漏

2024-09-29 17:21:51 发布

您现在位置:Python中文网/ 问答频道 /正文

我编写了一个简单的Python扩展模块来模拟3位模数转换器。它应该接受一个浮点数组作为它的输入,以返回相同大小的输出数组。输出实际上由量化的输入数字组成。以下是我的(简化)模块:

static PyObject *adc3(PyObject *self, PyObject *args) {
  PyArrayObject *inArray = NULL, *outArray = NULL;
  double *pinp = NULL, *pout = NULL;
  npy_intp nelem;
  int dims[1], i, j;

  /* Get arguments:  */
  if (!PyArg_ParseTuple(args, "O:adc3", &inArray))
    return NULL;

  nelem = PyArray_DIM(inArray,0); /* size of the input array */
  pout = (double *) malloc(nelem*sizeof(double));
  pinp = (double *) PyArray_DATA(inArray);

  /*   ADC action   */
  for (i = 0; i < nelem; i++) {
    if (pinp[i] >= -0.5) {
    if      (pinp[i] < 0.5)   pout[i] = 0;
    else if (pinp[i] < 1.5)   pout[i] = 1;
    else if (pinp[i] < 2.5)   pout[i] = 2;
    else if (pinp[i] < 3.5)   pout[i] = 3;
    else                      pout[i] = 4;
    }
    else {
    if      (pinp[i] >= -1.5) pout[i] = -1;
    else if (pinp[i] >= -2.5) pout[i] = -2;
    else if (pinp[i] >= -3.5) pout[i] = -3;
    else                      pout[i] = -4;
    }
  }

  dims[0] = nelem;

  outArray = (PyArrayObject *)
               PyArray_SimpleNewFromData(1, dims, NPY_DOUBLE, pout);
  //Py_INCREF(outArray);

  return PyArray_Return(outArray); 
} 

/* ==== methods table ====================== */
static PyMethodDef mwa_methods[] = {
  {"adc", adc, METH_VARARGS, "n-bit Analog-to-Digital Converter (ADC)"},
  {NULL, NULL, 0, NULL}
};

/* ==== Initialize ====================== */
PyMODINIT_FUNC initmwa()  {
    Py_InitModule("mwa", mwa_methods);
    import_array();  // for NumPy
}

我预计,如果引用计数处理正确,Python垃圾回收将(频繁地)释放输出数组使用的内存,如果它具有相同的名称并被重复使用。所以我用这个代码在一些虚拟的(但大量的)数据上进行了测试:

^{pr2}$

在这里,名为“b”的数组被多次重用,它的内存(由adc3()从堆中借用)将被返回给系统。我用gnome系统监视器来检查。与我的预期相反,python拥有的内存增长迅速,只能通过退出程序来释放(我使用IPython)。 为了比较,我用标准NumPy函数zeros()和copy()尝试了相同的过程:

for i in xrange(1000): 
    a = np.zeros(10000000)
    b = np.copy(a)
    print i

如您所见,后一个代码不会产生任何内存累积。 我在标准文档和网络上读了很多文本,试图使用Py_incrif(outArray),但没有使用它。一切都是徒劳的:问题依然存在。在

但是,我在http://wiki.scipy.org/Cookbook/C_Extensions/NumPy_arrays中找到了解决方案。 作者提供了一个扩展程序matsq(),它创建一个数组并返回它。当我试图使用作者建议的电话时:

outArray = (PyArrayObject *) PyArray_FromDims(nd,dims,NPY_DOUBLE);
pout = (double *) outArray->data;

而不是我的

pout = (double *) malloc(nelem*sizeof(double));
outArray = (PyArrayObject *)
            PyArray_SimpleNewFromData(1, dims, NPY_DOUBLE, pout);
/* no matter with or without Py_INCREF(outArray)) */

内存泄漏消失了!程序现在正常工作了。在

一个问题:有人能解释一下为什么PyArray_SimpleNewFromData()没有提供正确的引用计数,而PyArray_FromDims()却提供了正确的引用计数?在

非常感谢。在

添加。我可能在评论中超过了房间/时间,所以我把我的评论添加到Alex这里。 我试图这样设置OWNDATA标志:

outArray->flags |= OWNDATA;

但我得到了“错误:'OWNDATA'未声明”。 剩下的在评论里。提前谢谢你。在


已解决:标志的正确设置是

outArray->flags |= NPY_ARRAY_OWNDATA;

现在它起作用了。在

亚历克斯,对不起。在


Tags: 内存if数组nullelsedoublenpypout
1条回答
网友
1楼 · 发布于 2024-09-29 17:21:51

问题是,不是使用PyArray_SimpleNewFromData,它产生一个正确的refcounted PyObject*。相反,它与你的malloc,分配给pout,然后neverfreed

正如http://docs.scipy.org/doc/numpy/user/c-info.how-to-extend.html处的文档清楚地指出的那样,记录PyArray_SimpleNewFromData

the ndarray will not own its data. When this ndarray is deallocated, the pointer will not be freed. ... If you want the memory to be freed as soon as the ndarray is deallocated then simply set the OWNDATA flag on the returned ndarray.

(我强调不是)。你观察到的“将不会被释放”的行为被如此清楚地记录下来,并且没有采取特别推荐的步骤来避免这种行为。在

相关问题 更多 >

    热门问题