make - cpython repository




Почему размер 2⁶³ 36 байтов, а 2⁶³-1 составляет всего 24 байта? (2)

Все в Python является объектом. Таким образом, размер int в Python будет больше, чем обычно.

>>> sys.getsizeof(int())
24

Хорошо, но почему для 2⁶³ требуется еще 12 байтов по сравнению с 2⁶³ - 1 а не только один?

>>> sys.getsizeof(2**63)
36
>>> sys.getsizeof(2**62)
24

Я понимаю, что 2⁶³ - это long, а 2⁶³-1 - int, но почему 12 байтов разницы?

Не более интуитивно, я попробовал некоторые другие вещи:

>>> a = 2**63
>>> a -= 2**62
>>> sys.getsizeof(a)
36

a все еще сохраняется как long, даже если он может быть в int сейчас. Так что это не удивительно. Но:

>>> a -= (2**63 - 1)
>>> a = 2**63
>>> a -= (2**63 - 1)
>>> a
1L
>>> sys.getsizeof(a)
28

Новый размер.

>>> a = 2**63
>>> a -= 2**63
>>> a
0L
>>> sys.getsizeof(a)
24

Вернемся к 24 байтам, но все равно с длинным.

Последнее, что я получил:

>>> sys.getsizeof(long())
24

Вопрос:

Как работает память в этих сценариях?

Суб-вопросы:

Почему для добавления того, что нам подсказывает наша интуиция, всего 1 бит?

Почему int() и long() 24 байта, а long(1) - это уже 28 байтов, а int(2⁶²) ?

NB: Python 3.X работает немного по-другому, но не более интуитивно. Здесь я сосредоточился на Python 2.7; Я не тестировал на предыдущих версиях.


почему он получает еще 12 байтов за 2⁶³ по сравнению с 2⁶³ - 1, а не только один?

В системе LP64 1 Python 2 int состоит ровно из трех частей размером с указатель:

  • указатель типа
  • счетчик ссылок
  • фактическое значение, C long int

Это всего 24 байта. С другой стороны, Python long состоит из :

  • указатель типа
  • счетчик ссылок
  • число цифр, целое число размером с указатель
  • встроенный массив цифр значений, каждая из которых содержит 30 битов значения, но хранится в 32-битных единицах (один из неиспользуемых битов используется для эффективного переноса / заимствования при сложении и вычитании)

2 ** 63 требует 64 бит для хранения, поэтому он умещается в три 30-битные цифры. Поскольку каждая цифра имеет ширину 4 байта, вся long Python займет 24 + 3 * 4 = 36 байтов.

Другими словами, разница заключается в том, что long приходится отдельно хранить размер числа (8 дополнительных байтов), а также из-за того, что он занимает немного меньше места при хранении значения (12 байтов для хранения цифр 2 ** 63). С учетом размера значение 2 ** 63 в long занимает 20 байтов. Сравнивая это с 8 байтами, занятыми любым значением простого int получаем наблюдаемую 12-байтовую разницу.

Стоит отметить, что Python 3 имеет только один целочисленный тип, называемый int , который имеет переменную ширину, и реализован так же, как Python 2 long .

1 64-битная Windows отличается тем, что она сохраняет 32-битное long int , предположительно для совместимости с исходным кодом, с большим объемом старого кода, который использовал char , short и long как «удобные» псевдонимы для 8, 16 и 32-битных значения, которые работают на 16 и 32-битных системах. Чтобы получить действительный 64-битный тип в Windows x86-64, необходимо использовать __int64 или (в более новых версиях компилятора) long long или int64_t . Поскольку Python 2 внутренне зависит от установки Python int в C long в разных местах, sys.maxint остается 2**31-1 даже в 64-битной Windows. Эта причуда также исправлена ​​в Python 3, который не имеет понятия maxint .


Хотя я не нашел его в документации, вот мое объяснение.

Python 2 неявно продвигает int к long , когда значение превышает значение, которое можно сохранить в int. Размер нового типа ( long ) - это размер по умолчанию long , который равен 32. С этого момента размер вашей переменной будет определяться ее значением, которое может увеличиваться и уменьшаться.

from sys import getsizeof as size
a = 1
n = 32

# going up
for i in range(10):
    if not i:
        print 'a = %100s%13s%4s' % (str(a), type(a), size(a))
    else:
        print 'a = %100s%14s%3s' % (str(a), type(a), size(a))
    a <<= n

# going down
for i in range(11):
    print 'a = %100s%14s%3s' % (str(a), type(a), size(a))
    a >>= n


a =                                                                                                    1 <type 'int'>  24
a =                                                                                           4294967296 <type 'long'> 32
a =                                                                                 18446744073709551616 <type 'long'> 36
a =                                                                        79228162514264337593543950336 <type 'long'> 40
a =                                                              340282366920938463463374607431768211456 <type 'long'> 44
a =                                                    1461501637330902918203684832716283019655932542976 <type 'long'> 48
a =                                           6277101735386680763835789423207666416102355444464034512896 <type 'long'> 52
a =                                 26959946667150639794667015087019630673637144422540572481103610249216 <type 'long'> 56
a =                       115792089237316195423570985008687907853269984665640564039457584007913129639936 <type 'long'> 60
a =              497323236409786642155382248146820840100456150797347717440463976893159497012533375533056 <type 'long'> 64
a =    2135987035920910082395021706169552114602704522356652769947041607822219725780640550022962086936576 <type 'long'> 68
a =              497323236409786642155382248146820840100456150797347717440463976893159497012533375533056 <type 'long'> 64
a =                       115792089237316195423570985008687907853269984665640564039457584007913129639936 <type 'long'> 60
a =                                 26959946667150639794667015087019630673637144422540572481103610249216 <type 'long'> 56
a =                                           6277101735386680763835789423207666416102355444464034512896 <type 'long'> 52
a =                                                    1461501637330902918203684832716283019655932542976 <type 'long'> 48
a =                                                              340282366920938463463374607431768211456 <type 'long'> 44
a =                                                                        79228162514264337593543950336 <type 'long'> 40
a =                                                                                 18446744073709551616 <type 'long'> 36
a =                                                                                           4294967296 <type 'long'> 32
a =                                                                                                    1 <type 'long'> 28

Как вы можете видеть, тип остается long после того, как он сначала стал слишком большим для int , и начальный размер был 32, но размер изменяется со значением (может быть выше или ниже [или равен, очевидно] до 32)

Итак, чтобы ответить на ваш вопрос, базовый размер равен 24 для int и 28 для long , тогда как long также имеет место для сохранения больших значений (которые начинаются как 4 байта - следовательно, 32 байта для long , но могут увеличиваться и уменьшаться в соответствии с к стоимости)

Что касается вашего подвопроса, создание уникального типа (с уникальным размером) для нового числа невозможно, поэтому в Python есть «подклассы» long типа, которые имеют дело с диапазоном чисел, поэтому, как только вы превысите предел из вашего старого long вы должны использовать более новый, который также учитывает гораздо большие числа, поэтому он имеет на несколько байтов больше.





python-internals