2014-12-18 22 views
0

我在Python和C中有豐富的經驗,但是對於將C模塊作爲C函數的包裝器件編寫爲新手。對於一個項目,我需要一個名爲「score」的函數,它的運行速度比我能夠在python中獲得的速度快得多,所以我使用C語言編寫了代碼,並且從字面上來說只是想從python中調用它。它需要一個python的整數列表,我希望C函數得到一個整數數組,這個數組的長度,然後返回一個整數回到python。這是我目前的(工作)解決方案。Python C包裝內存泄漏

static PyObject *module_score(PyObject *self, PyObject *args) { 
    int i, size, value, *gene; 
    PyObject *seq, *data; 

    /* Parse the input tuple */ 
    if (!PyArg_ParseTuple(args, "O", &data)) 
     return NULL; 
    seq = PySequence_Fast(data, "expected a sequence"); 
    size = PySequence_Size(seq); 

    gene = (int*) PyMem_Malloc(size * sizeof(int)); 
    for (i = 0; i < size; i++) 
     gene[i] = PyInt_AsLong(PySequence_Fast_GET_ITEM(seq, i)); 

    /* Call the external C function*/ 
    value = score(gene, size); 

    PyMem_Free(gene); 

    /* Build the output tuple */ 
    PyObject *ret = Py_BuildValue("i", value); 
    return ret; 
} 

這工作,但似乎泄漏內存,並在我不能忽視的速度。我通過暫時讓score函數返回0並仍然看到泄漏行爲,確保泄漏發生在顯示的函數中。我曾經想過,對PyMem_Free的調用應該照顧PyMem_Malloc的存儲,但是我目前的猜測是這個函數中的某些東西在每次調用中都得到分配和保留,因爲泄漏行爲與調用此函數的次數成正比。我是不是正確執行數組轉換的序列,或者我可能無效地返回了結尾值?任何幫助表示讚賞。

+0

它認爲,Python有一個內存池,並呼籲'PyMem_Free'不會立即釋放指針。它將內部釋放它,以便Python可以在不重新分配的情況下重用它。但是,我不確定。 – 2014-12-18 23:02:55

+0

你在Linux上嗎?你怎麼確定有泄漏? – 2014-12-18 23:03:59

+0

我在Windows上使用cygwin,這是一個類似Linux的環境。我使用Windows任務管理器和頂部來看到Python進程吃越來越多的內存,然後隨機等待打印,看看只有當這個而不是其他的Python函數被調用時,內存纔會增長。 – hackartist 2014-12-18 23:08:16

回答

2

seq是一個新的Python對象,因此您需要刪除該對象。您應該檢查seq是否也是NULL。

喜歡的東西(未經測試):

static PyObject *module_score(PyObject *self, PyObject *args) { 
    int i, size, value, *gene; 
    long temp; 
    PyObject *seq, *data; 

    /* Parse the input tuple */ 
    if (!PyArg_ParseTuple(args, "O", &data)) 
     return NULL; 
    if (!(seq = PySequence_Fast(data, "expected a sequence"))) 
     return NULL; 

    size = PySequence_Size(seq); 

    gene = (int*) PyMem_Malloc(size * sizeof(int)); 
    for (i = 0; i < size; i++) { 
     temp = PyInt_AsLong(PySequence_Fast_GET_ITEM(seq, i)); 
     if (temp == -1 && PyErr_Occurred()) { 
      Py_DECREF(seq); 
      PyErr_SetString(PyExc_ValueError, "an integer value is required"); 
      return NULL; 
     } 
     /* Do whatever you need to verify temp will fit in an int */ 
     gene[i] = (int*)temp; 
    } 

    /* Call the external C function*/ 
    value = score(gene, size); 

    PyMem_Free(gene); 
    Py_DECREF(seq): 

    /* Build the output tuple */ 
    PyObject *ret = Py_BuildValue("i", value); 
    return ret; 
} 
+0

對不起,我是新來的...如何刪除seq對象?我沒有看到你的例子中的任何地方,你刪除它。除非它是用calloc或malloc創建的,否則不應該被釋放? – hackartist 2014-12-18 23:20:17

+0

我更新了答案。要刪除一個Python對象,使用'Py_DECREF()'。我還包含了一個檢查'PyInt_AsLong()'的返回值,並添加了一個地方來驗證在轉換爲int之前值的大小。 – casevh 2014-12-18 23:30:26

+0

謝謝,就是這樣!現在沒有更多的泄漏內存。 – hackartist 2014-12-19 00:48:16