В 64-битной системе целое число в Python занимает 24 байта. Это в 3 раза больше памяти, которая потребуется в, например, C для 64-битного целого. Теперь, я знаю, это потому, что целые числа Python являются объектами. Но для чего нужна дополнительная память? У меня есть свои догадки, но было бы хорошо знать наверняка.
Почему в Python требуется в три раза больше памяти?
Ответ 1
Помните, что тип Python int
не имеет ограниченного диапазона, как у C int
; единственным ограничением является доступная память.
Память уходит на хранение значения, текущего размера целочисленного хранилища (размер хранилища является переменным для поддержки произвольных размеров) и стандартной бухгалтерии объекта Python (ссылка на соответствующий объект и счетчик ссылок).
Вы можете найти источник longintrepr.h
(тип Python 3 int
традиционно назывался long
типом в Python 2); он эффективно использует тип PyVarObject
C для отслеживания целочисленного размера:
struct _longobject {
PyObject_VAR_HEAD
digit ob_digit[1];
};
Массив ob_digit
хранит "цифры" шириной 15 или 30 бит (в зависимости от вашей платформы); поэтому в моей 64-битной системе OS X целое число до (2 ^ 30) - 1 использует 1 цифру:
>>> sys.getsizeof((1 << 30) - 1)
28
но если вы используете 2 30-битные цифры в номере, понадобятся дополнительные 4 байта и т.д.:
>>> sys.getsizeof(1 << 30)
32
>>> sys.getsizeof(1 << 60)
36
>>> sys.getsizeof(1 << 90)
40
Основными 24 байтами являются структура PyObject_VAR_HEAD
, содержащая размер объекта, счетчик ссылок и указатель типа (каждые 8 байтов /64 бита на моей 64-битной платформе OS X).
В Python 2 целые числа <= sys.maxint
но> = -sys.maxint - 1
хранятся с использованием более простой структуры, хранящей только одно значение:
typedef struct {
PyObject_HEAD
long ob_ival;
} PyIntObject;
поскольку вместо PyVarObject
используется PyObject
в PyVarObject
нет поля ob_size
а размер памяти ограничен всего 24 байтами; 8 для long
значения, 8 для счетчика ссылок и 8 для указателя объекта типа.
Ответ 2
Из longintrepr.h мы видим, что объект "int" Python определен с помощью этой структуры C:
struct _longobject {
PyObject_VAR_HEAD
digit ob_digit[1];
};
Digit - это 32-разрядное значение без знака. Основная часть пространства занимает заголовок объекта с переменным размером. Из object.h мы можем найти его определение:
typedef struct {
PyObject ob_base;
Py_ssize_t ob_size; /* Number of items in variable part */
} PyVarObject;
typedef struct _object {
_PyObject_HEAD_EXTRA
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;
} PyObject;
Мы видим, что мы используем 64-разрядную версию Py_ssize_t, предполагающую 64-битную систему, для хранения количества цифр в значении. Это возможно расточительно. Мы также видим, что общий заголовок объекта имеет 64-битный счетчик ссылок и указатель на тип объекта, который также будет 64-разрядным хранилищем. Счетчику ссылок необходимо, чтобы Python знал, когда следует освободить объект, и указатель на тип объекта необходим, чтобы знать, что у нас есть int, а не, скажем, строка, поскольку структуры C не имеют возможности протестировать тип объект от произвольного указателя.
_PyObject_HEAD_EXTRA не имеет ничего общего с большинством построений python, но может использоваться для хранения связанного списка всех объектов Python в куче, если сборка включает этот параметр, используя еще два указателя по 64 бита каждый.