Понимание распределения памяти для больших целых чисел в Python

Как Python выделяет память для больших целых чисел?

Тип int имеет размер 28 bytes, и по мере увеличения значения int размер увеличивается с шагом 4 bytes.

  • Почему 28 bytes изначально для любого значения с 1?

  • Почему приращения 4 bytes?

PS: Я запускаю Python 3.5.2 на x86_64 (64-разрядная машина). Любые указатели/ресурсы/PEP о том, как работают (3.0+) интерпретаторы на таких огромных числах, - это то, что я ищу.

Код, иллюстрирующий размеры:

>>> a=1
>>> print(a.__sizeof__())
28
>>> a=1024
>>> print(a.__sizeof__())
28
>>> a=1024*1024*1024
>>> print(a.__sizeof__())
32
>>> a=1024*1024*1024*1024
>>> print(a.__sizeof__())
32
>>> a=1024*1024*1024*1024*1024*1024
>>> a
1152921504606846976
>>> print(a.__sizeof__())
36

Ответ 1

Почему 28 байт изначально для любого значения, равного 1?

Я считаю, @bgusach ответил, что полностью; Python использует C structs для представления объектов в мире Python, любых объектов включая int s:

struct _longobject {
    PyObject_VAR_HEAD
    digit ob_digit[1];
};

PyObject_VAR_HEAD - это макрос, который при добавлении добавляет другое поле в struct (поле PyVarObject, который специально используется для объектов, которые имеют некоторое понятие длины), и ob_digits - массив, содержащий значение для числа. Размер котельной зависит от этой структуры, для небольших и больших чисел Python.

Почему приращения 4 байтов?

Потому что, когда создается большее число, размер (в байтах) является кратным значению sizeof(digit); вы можете увидеть, что в _PyLong_New, где выделение памяти для нового longobject выполняется с помощью PyObject_MALLOC:

/* Number of bytes needed is: offsetof(PyLongObject, ob_digit) +
   sizeof(digit)*size.  Previous incarnations of this code used
   sizeof(PyVarObject) instead of the offsetof, but this risks being
   incorrect in the presence of padding between the PyVarObject header
   and the digits. */
if (size > (Py_ssize_t)MAX_LONG_DIGITS) {
    PyErr_SetString(PyExc_OverflowError,
                    "too many digits in integer");
    return NULL;
}
result = PyObject_MALLOC(offsetof(PyLongObject, ob_digit) +
                         size*sizeof(digit));

offsetof(PyLongObject, ob_digit) - это "котельная плита" (в байтах) для длинного объекта, которая не связана с сохранением ее значения.

digit определяется в файле заголовка, содержащем struct _longobject как typedef для uint32:

typedef uint32_t digit;

и sizeof(uint32_t) - это 4 байты. То, что количество, на которое вы увидите размер в байтах, увеличивается, когда аргумент size в _PyLong_New увеличивается.


Конечно, это именно то, как C Python решил реализовать его. Это деталь реализации, и поэтому вы не найдете много информации в PEP. Список рассылки python-dev проведет обсуждения по реализации, если вы найдете соответствующий поток:-).

В любом случае, вы можете найти другое поведение в других популярных реализациях, поэтому не принимайте это как должное.

Ответ 2

Это на самом деле легко. Python int - это не тот примитив, с которым вы можете привыкнуть с других языков, но полноценный объект с его методами и всеми вещами. Именно здесь происходят издержки.

Затем у вас есть полезная нагрузка, представляемое целое. И для этого нет предела, кроме вашей памяти.

Размер Python int - это то, что ему нужно, чтобы представить число плюс небольшие накладные расходы.

Если вы хотите прочитать далее, посмотрите соответствующую часть документации:

Целые числа имеют неограниченную точность