Расширение Python C - утечки памяти - PullRequest
0 голосов
/ 22 февраля 2019

Я относительно новичок в Python, и это моя первая попытка написать расширение C.

Фон В моем проекте Python 3.X мне нужно загрузить и проанализировать большой двоичный файлфайлы (10-100 МБ) для извлечения данных для дальнейшей обработки.Двоичный контент организован в кадры: заголовки, за которыми следует переменный объем данных.Из-за низкой производительности в Python я решил пойти на расширение C, чтобы ускорить загрузку.

Автономный код C превосходит Python в 20–500 раз, поэтому я вполне доволен им.

Проблема: память продолжает расти, когда я несколько раз вызываю функцию из моего C-расширения в одном и том же модуле Python.


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 free(X) в C. Если я явно освобождаю массивы в C, я столкнулся с segfault при попытке загрузить второй файл в цикле for в test.py,

Есть идеи, что я делаю не так?

1 Ответ

0 голосов
/ 22 февраля 2019

Это похоже на катастрофу управления памятью.NPY_ARRAY_OWNDATA должен вызвать free для данных (или, по крайней мере, PyArray_free, что не обязательно одно и то же ...).

Однако, как только это будет сделано, вы по-прежнему имеют глобальные переменные X и Y, указывающие на недопустимую область памяти.Затем вы вызываете realloc на этих недействительных указателях.На этом этапе вы хорошо знакомы с неопределенным поведением, и поэтому может произойти все что угодно.


Если это глобальная переменная, то память должна управляться глобально, а не Numpy.Если память управляется массивом Numpy, то вам нужно убедиться, что у вас нет другого способа доступа к ней, кроме как через этот массив Numpy.Все остальное вызовет у вас проблемы.

...