继承自 int 的 Cython 扩展类型导致 MemoryError

Posted

技术标签:

【中文标题】继承自 int 的 Cython 扩展类型导致 MemoryError【英文标题】:Cython Extension Type inheriting from int cause a MemoryError 【发布时间】:2015-08-19 05:08:51 【问题描述】:

我正在尝试创建一个继承自 int 或 cython.int 的扩展类型。这对我来说是必要的,因为我需要能够将此类型用作某些列表/数组的索引。

这是在 Anaconda 上使用 Cython v0.22 重现 Python 2.7.9 Win32(我正在运行 Windows 7)上的错误的代码:

import cython
cimport cython


import sys


cdef class ExtendedInt(int):


    #cdef object __weakref__ # explicitly enable weakref for this extension type, will self-destruct when it is no longer strongly referenced.


    def __add__(a, b):
        return a+b


# Simple test case
def main():
    total_it = 1000
    for i in xrange(total_it):
        for j in xrange(10000000):
            ExtendedInt(j)
        sys.stdout.write("\rGenerating lists of ExtendedInt : %i/%i" % (i, total_it))

如果您尝试创建大量 ExtendedInt,会发生什么情况,Python 解释器在某些时候会因 MemoryError 而崩溃。使用上面的代码 sn-p,在我的具有 4 GB 内存的机器上,它在第 11 次迭代时崩溃,但这会因机器规格而异。我尝试启用weakref,但没有解决问题。

但是,如果您将“cdef class ExtendedInt(int):”替换为“class ExtendedInt(int)”(因此它不再是扩展类型而是简单的 Python 类),那么问题就不会发生,有没有 MemoryError 崩溃。

因此,从整数继承的扩展类型似乎没有被正确释放,即使我启用了 weakref。这是我应该在跟踪器上填写的错误还是我遗漏了什么?

这是当前 Cython 版本的错误吗?我在 bugtracker 上找不到任何参考资料……或者我遗漏了一些可以解决问题的东西?

【问题讨论】:

你试过cdef class ExtendedInt(cython.int): 吗?你提到了它,但在你的例子中,你似乎得到了 Python int... 是的,我试过了,但效果并不好。无论如何,我确认这是当前 Cython 的一个错误,感谢邮件列表。 【参考方案1】:

Stefan Behnel discovered that the bug is in Python 2.7 inside intobject.c:

您的扩展类型会自动继承“int_free”插槽 函数从它的基本类型开始,所以“int_dealloc()”中的“else”情况 实际上会调用“int_free()”并将对象附加到空闲列表中 尽管 not 完全属于 PyInt 类型。然后,当创建一个新的 ExtendedInt 实例,Python 的 int-subtype 实例化代码 忽略空闲列表,而是创建一个全新的对象。

因此,空闲列表不断增长,直到填满所有内存和 进程死亡。我创建了一个 CPython 票证。

这是link to the CPython ticket。

这是 Guido Van Rossum 的答案:

预期的解决方案是要求 int 子类覆盖 tp_free。

请注意,大部分代码都是在假设以下情况下编写的 子类是使用类语句创建的(在常规 Python 模块,而不是 Cython)负责处理所有这些细节。那不 意味着 Cython 尝试这个是错误的,但这确实意味着没有很多 的文件,这也意味着我不认为你的事情 被报告为 CPython 中的错误。

所以我不确定这是否会得到修复,或者 Cython 是否会实施解决方法。无论如何,对于那些和我遇到同样问题的人,你可以通过从 object 继承来解决这个问题,然后重新实现整数的魔法方法,比如 intindex添加等,正如我在a concrete example here 中解释的那样。

【讨论】:

以上是关于继承自 int 的 Cython 扩展类型导致 MemoryError的主要内容,如果未能解决你的问题,请参考以下文章

Cython 将扩展模块传递给 python 导致 to_py_call_code 错误

cython 继承

在 Cython 中包装自定义类型 C++ 指针

《Cython系列》4. Cython中的扩展类

Cython:具有自定义参数类型的 std::function 回调

通过pickle保存cython扩展