Почему у моего модуля C течет память? - PullRequest
2 голосов
/ 10 февраля 2011

Я читаю списки из большого файла, который в итоге хочу сохранить как array.array s. Поскольку

map(int, line.split())

очень медленно, я написал небольшой модуль C, который делает strtok и более быструю версию atoi:

inline long
minhashTables_myatoi(const char* s)
{
    int r;
    for (r = 0; *s; r = r * 10 + *s++ - '0');
    return r;
}

static PyObject*
minhashTables_ints(PyObject *self, PyObject *args)
{
    char* s;
    Py_ssize_t slen;

    if(!PyArg_ParseTuple(args, "s#", &s, &slen))
        return NULL;

    long* buf = malloc(sizeof(long) * (slen+1)/2);

    const char* tok = strtok(s, " ");
    buf[0] = minhashTables_myatoi(tok);
    Py_ssize_t i;
    for(i = 1; (tok = strtok(NULL, " ")) != NULL; i++)
        buf[i] = minhashTables_myatoi(tok);

    Py_ssize_t buflen = i;
    PyObject* list = PyList_New(buflen);
    PyObject *o;
        for(i = 0; i < buflen; i++)
    {
        o = PyInt_FromLong(buf[i]);
        PyList_SET_ITEM(list, i, o);
    }
    free(buf);

    return list;
}

Так что мой скрипт на python вызывает ints() со строкой, передает ее конструктору array.array и сохраняет полученный массив в list.

Моя проблема в том, что теперь скрипт выполняет утечку памяти, чего, конечно же, не было с картой вместо функции ints().

Кроме того, использование моей собственной версии Pythons int() с использованием модуля C не приводит к утечке памяти.

Спасибо за вашу помощь!

Edit: Для проверки модуля я использовал этот скрипт:

import minhashTables

data = ' '.join(map(str, range(10)))
print 'start'
foo = minhashTables.ints(data)
del data
del foo
print 'stop'

И я запускаю valgrind --tool=memcheck --leak-check=full --show-reachable=yes python test.py, но там нет выхода из valgrind между start и stop, потому что есть тонны до и после.

Редактировать: Код для подтверждения утечки: импорт minhashTables

for i in xrange(1000000000):
    data = ' '.join(map(str, range(10, 10000)))
    foo = minhashTables.ints(data)

Я должен воссоздать строку, потому что strtok меняет ее. Кстати, копирование строки в другую ячейку памяти не меняет поведения.

Ответы [ 3 ]

2 голосов
/ 10 февраля 2011

Я предлагаю вам взглянуть на Valgrind - это очень полезный инструмент для выявления утечек памяти в C.

1 голос
/ 10 февраля 2011

Вам действительно нужно malloc отключить место для всех этих long с?

Я не знаком с API-интерфейсом Python / C, так что это может быть ужасным советом, но разве вы не можете просто перебирать строку и добавлять каждую длинную строку в список по ходу дела?

т.е. возьми этот код:

static const char* const testString = "12 345  67  8 910 11 1213 141516, 1718";

int main()
{
    const char* i = testString;
    long parseLong = 0;
    int gotLong = 0;

    for (;*i;++i)
    {
        if ('0' <= *i && *i <= '9')
        {
            parseLong = (parseLong * 10) + (*i - '0');
            gotLong = 1;
        }
        else if (gotLong)
        {
            printf("Got: %d\n", parseLong);
            parseLong = 0;
            gotLong = 0;
        }
    }

    if (gotLong)
        printf("Got: %d\n", parseLong);
}

А затем замените printf на какое-нибудь подходящее питонское совершенство, например PyList_Append().

Помимо того, что он избегает malloc, использует меньше памяти и может безопасно работать непосредственно с постоянной строкой Python, этот код также обрабатывает угловые случаи, такие как пустые строки, несколько пробелов и другие разделители между числами.


Редактировать: Подсчет длинных Если вы хотите сначала посчитать количество длинных, чтобы вы могли выделить правильную длину списка Python, то вы можете просто добавить что-то вроде этого:
    for (i = testString;*i;++i)
    {
        const int isdigitoflong = isdigit(*i);

        if (!gotLong && isdigitoflong)
            longCount++;

        gotLong = isdigitoflong;
    }

Что должно быть относительно быстро.


Редактировать 2: улучшенный парсер
Вот немного лучшая версия парсера выше, она немного компактнее, ей не нужно gotLong и не нужно повторять код, чтобы справиться с финальным длинным:
    for (i = testString;*i;++i)
    {
        if (isdigit(*i))
        {
            do {
                parseLong = (parseLong * 10) + (*i - '0');
            } while (*++i && isdigit(*i));

            printf("Got: %d\n", parseLong);
            parseLong = 0;
        }
    }   
0 голосов
/ 10 февраля 2011

Попробуйте это

inline long
    minhashTables_myatoi(const char* s)
    {
        long result=0;
        while((*s)!='\0'){
            result = result * 10 + (*s- '0');
            s++;
        }
        return result;
    }
...