我最近开始使用Boost's Python库来包装相当大的C ++库的部分内容。
很偶然,我发现Boost Python创建的每个Python对象至少都比Python List的大小大。
使用noddy_NoddyType
example中的C-Python API docs,我可以非常轻松地公开新的对象类型实例noddy_NoddyObject
。
使用原始C-Python API,这与自定义Python对象一样小而简单: -
>>> import noddy, sys
>>> print sys.getsizeof( noddy.Noddy ) # PyTypeObject size?
872
>>> print sys.getsizeof( noddy.Noddy() ) # PyObject size
16
与基础对象进行比较: -
>>> print sys.getsizeof( object )
872
>>> print sys.getsizeof( object() )
16
这是预期的,但是当我使用Boost Python公开noddy_NoddyObject
时呢?
>>> print sys.getsizeof( noddy.Noddy )
904
>>> print sys.getsizeof( noddy.Noddy() )
80
说什么?!!那不太理想......对象实例比它们需要的大5倍!
(这在非64位计算机上会有所不同。)
在Boost Python维基上,有额外膨胀的some explanation: -
内存消耗
通常,带有相应Python对象的包装C ++对象是 大小:
- 一个新式的类(派生自Python中的'object') 实例加上
- 允许可变长度数据所需的额外大小 在实例中,加上
- C ++对象的大小,加上
vtable指针的大小,加上指向C ++对象的指针 instanceholder,加上- 确保需要零个或多个填充字节 实例持有者正确对齐。
您可以在
boost/python/object/instance.hpp
中看到这一点。对于某些C ++类instance<value_holder<T> >
,大多数Python对象由T
表示。
但是,显然没有迹象表明如何减少实例大小,以及如何使用自定义PyTypeObject
。
因此,在instance.hpp
中,您可以看到所有boost::python::instance
在其基类声明中使用PyObject_VAR_HEAD
。这可以解释Boost Python编译的noddy_NoddyObject
的大小与Python list
或numpy.array
的大小更接近的相似性: -
>>> print sys.getsizeof( list )
872
>>> print sys.getsizeof( list() )
72
>>> print sys.getsizeof( numpy.array )
72
>>> print sys.getsizeof( numpy.array() )
80
(哦,这很有意思......如果我没弄错的话,numpy.array
是最小PyObject
实例的大小,它比PyTypeObject
小得多。我想知道是什么其中的含义是..)
除了对象大小之外,我在这里引起的关注是堆事务。我有一个C ++基类,它几乎只包含与内存管理相关的功能:它有new
和delete
运算符;大量与引用计数相关的方法,并且由大约一百个其他C ++类继承。
曾经读过C-Python API docs on Defining new Types,我觉得这里有一种新的类型。通过定义一个新的PyTypeObject
结构,并将其成员(tp_alloc
等)指向C ++类的运算符和成员函数,我想我可以让Python解释器直接调用C ++成员函数。派生类类型在典型的boost::python::object
实例和(已经管理的)C ++类之间没有重复的功能。
使用bp::class_<>
模板,我认为C ++代码和Python代码将分别管理内存到类实例,这看起来可能非常低效。
PyTypeObject
通过使用新的PyTypeObject
,我认为我的所有问题都将得到解决。简单对象的内存占用量可能要小得多(如果我可以将noddy_NoddyObject
降低到24个字节,我会非常高兴)并且似乎可以更灵活地管理内存。
那么,还有其他人觉得有必要定义一个新的PyTypeObject
,不同于Boost Python实例的默认值吗?怎么会这样呢?
通过采用更大的基础类型,似乎Boost正在失去效率和灵活性。减肥总是比获得更多更痛苦。