2011-02-10 2 views
2

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

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; 
} 

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

Моя проблема заключается в том, что в настоящее время сценарий утечки памяти, который он не сделал с картой, а не с функцией ints(), конечно.

Также, используя мою собственную версию Pythons int() с использованием модуля C, не происходит утечка памяти.

Благодарим за помощь!

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

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, через есть тонны до и после этого.

Edit: Код подтверждения травит: импорт minhashTables

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

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

+2

Возможно, вам захочется взглянуть на функции NumPy [loadtxt()] (http://docs.scipy.org/doc/numpy/reference/generated/numpy.loadtxt.html#numpy.loadtxt) и [ genfromtxt()] (http://docs.scipy.org/doc/numpy/reference/generated/numpy.genfromtxt.html#numpy.genfromtxt). Они довольно быстры. –

+0

Вы знаете о 's #', правильно? –

+0

Мне нужно хранить разные части файла в разных массивах, поэтому, к сожалению, я не хочу читать весь файл. Что такое #? – chuck

ответ

-1

Попробуйте

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

Можете ли вы уточнить, почему это исправит утечку памяти? – jhwist

+0

Это явно не устраняет утечку памяти и, более того, явно не лучше, чем исходная версия (тем более, что форматирование испорчено). Хотя я не могу поверить, что любая версия быстрее, чем 'atoi'. – jchl

+0

@jchl: Сначала я тоже не мог в это поверить, но время показало, что так оно и есть, поэтому я его сохранил. – chuck

2

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

+0

Вы имеете в виду под управлением valgrind на интерпретаторе python, который запускает мой скрипт? Я знаком с valgrind, но у меня проблемы с переходом на результат. – chuck

+0

Можете ли вы ограничить вывод, создав небольшой тестовый скрипт, содержащий только две ваши функции и тестовый жгут, а затем запустите valgrind на python, интерпретируя этот скрипт? – GrahamS

+0

Да. Я поставил его на вопрос выше. – chuck

1

ли вам действительно нужно malloc офф пространство для всех этих long s?

Я не знаком с 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 с некоторой подходящей pythony-благости, как PyList_Append().

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


Edit: Подсчет Longs Если вы хотите, чтобы посчитать количество длинных позиций первого, так что вы можете Alloc правильную длину списка Python, то вы можете просто добавить что-то вроде этого:

for (i = testString;*i;++i) 
    { 
     const int isdigitoflong = isdigit(*i); 

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

     gotLong = isdigitoflong; 
    } 

Который должен быть относительно быстрым.


Edit 2: Лучше Parser
Вот немного лучше версии парсера выше, это немного более компактный, не нуждается в 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

Мое намерение состояло в том, чтобы избежать избыточных ресурсов перераспределения 'PyList_Append()' (видел этот совет в другом вопросе). Мне нравится ваше решение, и я думаю, что я улажу его часть синтаксического анализа, хотя мне все равно не нужны уничтоженные или пустые строки. Как вы можете себе представить, что использование нескольких МБ памяти не является моей проблемой, если учесть накопленный размер утечки. – chuck

+0

@chuck: Да, я подумал, пытаетесь ли вы избежать этого. Я не уверен, как Python реализует списки, поэтому может стоить сравнительный анализ, чтобы увидеть, что представляет собой реальная разница в жизни. Также вы упомянули, что списки занимают несколько сотен МБ. Если каждая строка также огромна, вы можете обнаружить, что на самом деле быстрее запустить этот синтаксический анализатор за два прохода: сначала пройдите, чтобы просто подсчитать количество длин, затем выделите свой список и сделайте второй проход, чтобы на самом деле проанализировать их и поместить в список. – GrahamS

+0

@chuck: Я добавил код в свой ответ, чтобы показать, как можно быстро посчитать длинные строки в первом проходе. – GrahamS

Смежные вопросы