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

时间:2015-06-04 14:19:21

标签: python inheritance crash cython

我正在尝试创建一个继承自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。使用上面的代码片段,在我的具有4 GB内存的计算机上,它会在第11次迭代时崩溃,但这将根据计算机规格而有所不同。我尝试启用weakref,但它没有解决问题。

但是,如果你替换" cdef类ExtendedInt(int):"只需"类ExtendedInt(int)" (所以它不再是扩展类型,而是一个简单的Python类),那么问题就不会发生,没有MemoryError崩溃。

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

这是当前Cython版本的错误吗?我找不到关于bugtracker的任何参考......或者我错过了可以解决这个问题的东西?

1 个答案:

答案 0 :(得分:0)

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

  

您的扩展类型会自动继承“int_free”插槽   函数来自其基类型,因此“int_dealloc()”中的“else”情况   实际上会调用“int_free()”并将该对象追加到空闲列表中   尽管完全属于PyInt类型。然后,在创建新的时   ExtendedInt实例,Python的int-subtype实例化代码   忽略自由列表,而是创建一个全新的对象。

     

因此,自由列表不断增长,直到它填满所有内存和   过程死亡。我创建了一张CPython票。

这是link to the CPython ticket

以下是Guido Van Rossum的答案:

  

预期的解决方案是要求int子类重写   tp_free。

     

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

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