Расширение NumPy C API вызывает чрезмерное использование памяти - PullRequest
2 голосов
/ 02 июля 2019

Я написал расширение C для NumPy, чтобы ускорить некоторые из моих вычислений, но я все больше использую память, когда вызываю функцию несколько раз.Я урезал функцию до минимального примера:

PyObject* memory_test_function(PyObject* self, PyObject* args)
{
    PyArrayObject *ang;
    int i;

    if (!PyArg_ParseTuple(args, "O", &ang)) return NULL;

    int L0 = (int) PyArray_DIMS(ang)[0];

    // ballooning memory usage 
    npy_intp final_out_dims[2] = {L0,1};
    PyObject *output_array;
    output_array = PyArray_SimpleNew(2, final_out_dims, NPY_FLOAT64);
    Py_INCREF(output_array);

    for (i=0;i<L0;i++) 
    {
        *(double *)PyArray_GETPTR2(output_array,i,0) = 
            tan(*(double *)PyArray_GETPTR2(ang,i,0));
    }

    return PyArray_Return(output_array);

    /* constant memory usage
    double sum=0.0;
    for (i=0;i<L0;i++) sum+=tan(*(double *)PyArray_GETPTR2(ang,i,0));
    return PyFloat_FromDouble(sum); */
}

Проблема, по-видимому, вызвана созданием выходного массива, поскольку просто возвращение числа с плавающей запятой без создания объекта массива является постоянным в памяти.Я подозреваю, что есть проблема с INCREF / DECREF, но я думал, что все делал правильно.Повторный вызов этой функции (миллион или около того раз) приводит к линейному увеличению использования памяти со временем, что заставляет меня думать, что что-то неправильно собирается.Использование gc вручную не помогает.Пожалуйста, дайте мне знать, если есть что-то очевидное, что мне не хватает!

1 Ответ

1 голос
/ 02 июля 2019

PyArray_SimpleNew звонки (косвенно, вероятно) _Py_NewReference под капотом.Эта функция устанавливает счетчик ссылок для вновь созданной ссылки на 1.

Ваш последующий Py_INCREF увеличивает счетчик ссылок до 2 и таким образом гарантирует, что Python никогда не освободит этот объект, даже если все ссылки на него прекратятсясуществовать, потому что его счетчик ссылок никогда не упадет до 0.

Вызов Py_INCREF не нужен, потому что вы не делитесь ссылкой здесь с любым другим объектом, вы простоиспользуя его локально, а затем передавая его вызывающей стороне.

...