2

我正在从一个大文件中读取列表,我最终希望将其存储为array.arrays. 因为

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()函数。

同样使用我自己的使用 C 模块的 Python 版本int()不会泄漏内存。

谢谢你的帮助!

编辑: 要验证我使用此脚本的模块:

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 的输出,之前和之后都有很多。startstop

编辑:确认泄漏的代码:import minhashTables

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

我必须重新创建字符串,因为 strtok 更改了它。顺便说一句,将字符串复制到另一个内存位置不会改变行为。

4

3 回答 3

2

我建议你看一下Valgrind——它是一个非常有用的工具,可以深入了解 C 中的内存泄漏。

于 2011-02-10T10:01:49.553 回答
1

你真的需要malloc为所有这些longs 腾出空间吗?

我不熟悉 Python/C API,所以这可能是一个糟糕的建议,但你不能只遍历字符串并将你找到的每一个长字符串附加到列表中吗?

即拿这个代码:

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-goodness替换PyList_Append().

除了避免malloc使用更少的内存并能够安全地直接对常量 Python 字符串进行操作外,此代码还可以处理诸如空字符串、多个空格和其他数字之间的分隔符之类的极端情况。


编辑:计算 Longs 如果你想先计算 long 的数量,那么你可以分配 Python List 的正确长度,那么你可以添加如下内容:

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

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

        gotLong = isdigitoflong;
    }

这应该比较快。


编辑 2:更好的解析器
这是上面解析器的一个稍微好一点的版本,它更紧凑,不需要也不必gotLong重复代码来处理最后的 long:

    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;
        }
    }   
于 2011-02-10T15:00:43.740 回答
-1

尝试这个

inline long
    minhashTables_myatoi(const char* s)
    {
        long result=0;
        while((*s)!='\0'){
            result = result * 10 + (*s- '0');
            s++;
        }
        return result;
    }
于 2011-02-10T09:46:42.000 回答