Я недавно начал использовать Boost’s Python библиотека, чтобы обернуть части довольно большой библиотеки C ++.
Совершенно случайно я обнаружил, что каждый объект Python, созданный Boost Python, по крайней мере больше, чем размер списка Python.
С использованием noddy_NoddyType
пример от C-Python API документы, Я могу довольно легко выставить новый экземпляр типа объекта, 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 есть какое-то объяснение для дополнительного раздувания: —
Потребление памяти
Как правило, обернутый объект C ++ с соответствующим объектом Python
размер:
- класс нового стиля (производный от объекта в Python)
экземпляр плюс- дополнительный размер, необходимый для разрешения данных переменной длины
в случае, плюс- размер объекта C ++, плюс
размер указателя vtable, плюс указатель на объект C ++
Экземпляр, плюс- ноль или более байтов заполнения, необходимого для обеспечения
что держатель экземпляра правильно выровнен.Вы можете увидеть это в
boost/python/object/instance.hpp
, Большинство объектов Python представленыinstance<value_holder<T> >
для некоторого класса C ++T
,
Но в частности нет никаких указаний ни относительно того, как уменьшить размер экземпляра, ни как использовать пользовательский PyTypeObject
,
Итак, в instance.hpp
, Вы можете видеть, что все boost::python::instance
использовать PyObject_VAR_HEAD
в объявлении их базового класса. Это объяснило бы намного более близкое сходство в размере Boost 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
экземпляр, который намного меньше, чем 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 теряет эффективность и гибкость. Потерять вес всегда тяжелее, чем набирать больше.
Задача ещё не решена.
Других решений пока нет …