我最近开始使用Boost 的 Python库来包装一个相当大的 C++ 库的一部分。
很偶然,我发现 Boost Python 创建的每个 Python 对象至少比 Python 列表的大小要大。
简单类型
使用C-Python API 文档中的noddy_NoddyType
示例,我可以很容易地公开一个新的对象类型实例,.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
不出所料,但是当我noddy_NoddyObject
使用 Boost Python 公开时呢?
>>> print sys.getsizeof( noddy.Noddy )
904
>>> print sys.getsizeof( noddy.Noddy() )
80
说什么?!!这并不理想......对象实例比它们需要的大 5 倍!
(这在非 64 位机器上会有所不同。)
在 Boost Python wiki 上,有一些关于额外膨胀的解释:-
内存消耗
一般来说,一个带有对应 Python 对象的包装 C++ 对象的大小为:
- 一个新型类(派生自 Python 中的“对象”)实例加上
- 允许实例中的可变长度数据所需的额外大小,加上
- C++ 对象的大小,加上
vtable 指针的大小,加上指向 C++ 对象的实例持有者的指针,加上- 确保实例持有者正确对齐所需的零个或多个字节的填充。
你可以在
boost/python/object/instance.hpp
.instance<value_holder<T> >
对于某些 C++ 类,大多数 Python 对象由 , 表示T
。
但显然没有迹象表明如何减少实例大小,也没有说明如何使用自定义PyTypeObject
.
序列类型
因此,在 中,您可以在其基类的声明中instance.hpp
看到 allboost::python::instance
的使用。这可以解释 Boost Python-compiled的PyObject_VAR_HEAD
大小与 Python 的大小更相似,或者:-noddy_NoddyObject
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
实例的大小,比 a 小得多PyTypeObject
。我想知道那里的含义是什么..)
内存管理
除了对象大小之外,让我关注的问题是堆事务之一。我有一个 C++ 基类,它几乎完全由与内存管理相关的功能组成:它有new
和delete
运算符;大量与引用计数相关的方法,并被大约一百个其他 C++ 类继承。
曾经阅读过定义新类型的 C-Python API 文档,我认为这里适合使用新类型。通过定义一个新PyTypeObject
结构,并将其成员(tp_alloc
等)指向 C++ 类的运算符和成员函数,我想我可以让 Python 解释器直接调用 C++ 成员函数。派生类类型将不会在典型boost::python::object
实例和(已托管的)C++ 类之间复制任何功能。
使用bp::class_<>
模板,我认为 C++ 代码和 Python 代码都将分别管理类实例的内存,这似乎可能非常低效。
做一个新的PyTypeObject
通过使用新的PyTypeObject
,我认为我所有的担忧都会得到解决。简单对象的内存占用可能要小得多(如果我能noddy_NoddyObject
减少到 24 个字节,我会非常高兴),而且管理内存的方式似乎会有更大的灵活性。
那么,有没有其他人觉得需要定义一个新的PyTypeObject
,不同于 Boost Python 实例的默认值?怎么办?
通过使用更大的基本类型,Boost 似乎正在失去效率和灵活性。减肥总是比增重更痛苦。