使用 Python C API 将 Python 中的函数移植到 C 时遇到问题

发布于 2024-10-12 14:38:15 字数 865 浏览 6 评论 0原文

我在 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 版本返回截然不同的值。任何帮助将不胜感激。

I have a checksum function in 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

that I am trying to port to a C module for speed. Here's the C function:

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

I use it by opening a file and feeding it a 4096 block of data. They both return the same values for small strings, but when I feed it binary data straight from a file, the C version returns wildly different values. Any help would be appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

望她远 2024-10-19 14:38:15

我猜你的局部变量有某种溢出。 b 可能会变大。只需转储这些值以进行调试,您就应该看看这是否是问题所在。正如您提到的,出于性能原因您正在移植该方法。你检查过心理吗?可能足够快而且容易得多。还有更多其他工具可以将 Python 代码的一部分即时编译为 C,但我脑子里没有名字。

I would guess that you have some kind of overflow in your local variables. Probably b gets to large. Just dump the values for debugging purposes and you should see if it's the problem. As you mention, that you are porting the method for performance reasons. Have you checked psyco? Might be fast enough and much easier. There are more other tools which compile parts of python code on the fly to C, but I don't have the names in my head.

你好,陌生人 2024-10-19 14:38:15

我建议原来的校验和函数是“不正确的”。校验和返回的值的大小不受限制(对于以 MB 为单位的任何给定大小,您可以构造一个输入,其校验和至少为该大小)。如果我的计算正确,对于小于 260 MB 的输入,该值可以容纳在 64 位中,并且 b 可以容纳小于 4096 字节的整数。现在,我可能对这个数字不满意,但这意味着对于更大的输入,这两个函数保证以不同的方式工作。

要将第一个函数转换为 C 语言,您需要将 bc 保留为 Python 整数,并以 Python 表达式的形式执行最后一个计算。不过,这可以改进:

  • 您可以使用 C long long 变量来存储中间和,并在一定次数的迭代后将其添加到 Python 整数中。如果迭代次数为 n,则 a 的最大值为 n * 255b 的最大值为len(数据)* n * 255。将它们存储在 C long long 变量中时,尝试将它们保留在 2**63-1 下。
  • 您可以使用 long long 而不是 unsigned long long,并在每次在调试模式下变为负值时引发 RuntimeError

另一种解决方案是使用 a & 将 Python 等效项限制为 64 位。 0xffffffffffffffffb & 0xffffffffffffffff

最好的解决方案是使用另一种校验和,例如 binascii.crc32。

I'd suggest that the original checksum function is "incorrect". The value returned for checksum is of unlimited size (for any given size in MB, you could construct an input for which the checksum will be at least of this size). If my calculations are correct, the value can fit in 64 bits for inputs of less than 260 MB, and b can fit in an integer for anything less than 4096 bytes. Now, I might be off with the number, but it means that for larger inputs the two functions are guaranteed to work differently.

To translate the first function to C, you'd need to keep b and c in Python integers, and to perform the last calculation as a Python expression. This can be improved, though:

  • You could use C long long variables to store an intermediate sum and add it to the Python integers after a certain number of iterations. If the number of iterations is n, the maximum value for a is n * 255, and for b is len(data) * n * 255. Try to keep those under 2**63-1 when storing them in C long long variables.
  • You can use long long instead of unsigned long long, and raise a RuntimeError every time it gets negative in debug mode.

Another solution would be to limit the Python equivalent to 64 bits by using a & 0xffffffffffffffff and b & 0xffffffffffffffff.

The best solution would be to use another kind of checksum, like binascii.crc32.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文