Проблемы с переносом функции в Python на C с использованием Python C API - PullRequest
3 голосов
/ 15 января 2011

У меня есть функция контрольной суммы в Python:

def checksum(data):
    a = b = 0
    l = len(data)
    for i in range(l):
        a += ord(data[i])
        b += (l - i)*ord(data[i])

    return (b << 16) | a, a, b

, которую я пытаюсь перенести на модуль C для скорости.Вот функция C:

static PyObject *
checksum(PyObject *self, PyObject *args)
{
    int i, length;
    unsigned long long a = 0, b = 0;
    unsigned long long checksum = 0;
    char *data;

    if (!PyArg_ParseTuple(args, "s#", &data, &length)) {
        return NULL;
    }

    for (i = 0; i < length; i++) {
        a += (int)data[i];
        b += (length - i) * (int)data[i];
    }

    checksum = (b << 16) | a;
    return Py_BuildValue("(Kii)", checksum, (int)a, (int)b);
}

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

Ответы [ 2 ]

1 голос
/ 15 января 2011

Я бы предположил, что исходная функция контрольной суммы "неверна".Значение, возвращаемое для контрольной суммы, имеет неограниченный размер (для любого заданного размера в МБ вы можете создать вход, для которого контрольная сумма будет, по крайней мере, такого размера).Если мои расчеты верны, значение может уместиться в 64 бита для входных данных менее 260 МБ, а b может уместиться в целое число для чего-либо менее 4096 байт.Теперь я могу не согласиться с числом, но это означает, что для больших входов две функции гарантированно будут работать по-разному.

Чтобы перевести первую функцию на C, вам нужно оставить b иc в целых числах Python и для выполнения последнего вычисления в качестве выражения Python.Это можно улучшить, хотя:

  • Вы можете использовать переменные C long long для хранения промежуточной суммы и добавления ее к целым числам Python после определенного числа итераций.Если число итераций равно n, максимальное значение для a равно n * 255, а для b равно len(data) * n * 255.Старайтесь хранить их в 2**63-1, сохраняя их в переменных C long long.
  • Вы можете использовать long long вместо unsigned long long и поднимать RuntimeError каждый раз, когда он становится отрицательным в режиме отладки.

Другим решением будет ограничение Pythonэквивалентно 64 битам с использованием a & 0xffffffffffffffff и b & 0xffffffffffffffff.

Наилучшим решением будет использование другого вида контрольной суммы, например binascii.crc32.

1 голос
/ 15 января 2011

Я предполагаю, что у вас есть какое-то переполнение в ваших локальных переменных.Вероятно, б добирается до большого.Просто сбросьте значения для целей отладки, и вы должны увидеть, если это проблема.Как вы упомянули, вы переносите метод по соображениям производительности.Вы проверяли психо?Может быть достаточно быстро и намного проще.Есть и другие инструменты, которые компилируют части кода Python на лету в C, но у меня нет имен в моей голове.

...