2017-10-26 2 views
2

파이썬 C++ 확장을 작성했지만 기능 중 하나에 문제가 있습니다. 이 확장에서 제공하는 함수는 2 개의 배열을 입력으로 사용하고 하나는 출력으로 생성합니다. 파이썬 C++ 확장 - 메모리 누수 또는 액세스 위반

나는 단지 함수의 코드

float* forward(float* input, float* kernels, npy_intp* input_dims, npy_intp* kernels_dims){ 

    float* output = new float[output_size]; 

    //some irrelevant matrix operation code 

    return output; 
} 

의 관련 부분 그리고 래퍼를 왼쪽했습니다

static PyObject *module_forward(PyObject *self, PyObject *args) 
{ 
    PyObject *input_obj, *kernels_obj; 

    if (!PyArg_ParseTuple(args, "OO", &input_obj, &kernels_obj)) 
     return NULL; 

    PyObject *input_array = PyArray_FROM_OTF(input_obj, NPY_FLOAT, NPY_IN_ARRAY); 
    PyObject *kernels_array = PyArray_FROM_OTF(kernels_obj, NPY_FLOAT, NPY_IN_ARRAY); 

    if (input_array == NULL || kernels_array == NULL) { 
     Py_XDECREF(input_array); 
     Py_XDECREF(kernels_array); 
     return NULL; 
    } 


    float *input = (float*)PyArray_DATA(input_array); 
    float *kernels = (float*)PyArray_DATA(kernels_array); 

    npy_intp *input_dims = PyArray_DIMS(input_array); 
    npy_intp *kernels_dims = PyArray_DIMS(kernels_array); 

    /////////THE ACTUAL FUNCTION 
    float* output = forward(input, kernels, input_dims, kernels_dims); 


    Py_DECREF(input_array); 
    Py_DECREF(kernels_array); 

    npy_intp output_dims[4] = {input_dims[0], input_dims[1]-kernels_dims[0]+1, input_dims[2]-kernels_dims[1]+1, kernels_dims[3]}; 

    PyObject* ret_output = PyArray_SimpleNewFromData(4, output_dims, NPY_FLOAT, output); 

    delete output;//<-----THE PROBLEMATIC LINE//////////////////////////// 

    PyObject *ret = Py_BuildValue("O", ret_output); 

    Py_DECREF(ret_output); 

    return ret; 
} 

마법 일이 어디 강조 delete 연산자는 다음과 같습니다없이이 기능 누출 메모리, 메모리 액세스 위반으로 인해 충돌합니다.

재미있는 점은 두 개의 배열을 반환하는 또 다른 메서드를 작성한 것입니다. 그래서 함수는 두 개의 플로트 * 요소를 가리키는 ** float를 반환

float** gradients = backward(input, kernels, grads, input_dims, kernel_dims, PyArray_DIMS(grads_array)); 

Py_DECREF(input_array); 
Py_DECREF(kernels_array); 
Py_DECREF(grads_array); 

PyObject* ret_g_input = PyArray_SimpleNewFromData(4, input_dims, NPY_FLOAT, gradients[0]); 
PyObject* ret_g_kernels = PyArray_SimpleNewFromData(4, kernel_dims, NPY_FLOAT, gradients[1]); 

delete gradients[0]; 
delete gradients[1]; 
delete gradients; 

PyObject* ret_list = PyList_New(0); 

PyList_Append(ret_list, ret_g_input); 
PyList_Append(ret_list, ret_g_kernels); 

PyObject *ret = Py_BuildValue("O", ret_list); 

Py_DECREF(ret_g_input); 
Py_DECREF(ret_g_kernels); 

return ret; 

주의 두 번째 예는 완벽하게, 아니 충돌하거나 메모리 누수, 작동 그들이 PyArray 객체에 내장 된 후에도 배열에 delete를 호출하는 동안 .

여기에 무슨 일이 일어나고 있는지 나에게 누군가 가르쳐 주시겠습니까? PyArray_SimpleNewFromDatadocs에서

답변

3

:

데이터 주위 래퍼 지정된 포인터가 가리키는 배열을 만듭니다.

으로 배열을 만들면 복사하지 않고 데이터 주위에 래퍼를 만듭니다. 즉, 래핑하는 데이터가 배열보다 오래 남아 있어야합니다. delete - 데이터 위반. 그냥 원래 데이터 래퍼를하지 않도록 당신은 다른 배열을 만들 수

  • :

    당신은 몇 가지 옵션이 있습니다.

  • 배열에 대한 액세스를 신중하게 제어하고 데이터 유효 기간이 끝나기 전에 delete 데이터를 종료 할 수 있습니다.
  • 개체의 수명이 끝나면 데이터를 소유하는 Python 개체를 만들고 delete 데이터를 가져오고 PyArray_SetBaseObject으로 해당 개체의 base 개체를 설정하면 개체 자체가 죽을 때까지 개체 개체가 유지됩니다.
+0

나는 그런 식으로 작동한다고 생각했지만 두 번째 예제는 동의하지 않습니다. 나는 포인터도 거기에서 삭제한다. 그리고 웬일인지 모든 것이 작동한다. – Lugi

+1

@Lugi : 정의되지 않은 동작은 충돌 할 필요가 없습니다. C++은 좋은 언어가 아닙니다. 코드가 충돌하지 않고 실행되는 것처럼 보이기 때문에 코드가 정확하다고 생각하지 마십시오. – user2357112

+0

예를 들어'PyArray_Zeros' 메소드를 사용하여 생성 된 배열을 C 배열의 원하는 값으로 채우고'PyArray_SimpleNewFromData'로 만든 배열은 똑같이 동작하지 않을까요? – Lugi