Рубрики
Без рубрики

Python Кэширует Целые Числа

Целое число в Python не является традиционной реализацией 2, 4 или 8 байт, а скорее реализовано в виде массива цифр в базе 2^30, что позволяет Python поддерживать сверхдлинные целые числа…

Автор оригинала: Arpit Bhayani.

Целое число в Python не является традиционной реализацией 2, 4 или 8 байт, а скорее реализовано в виде массива цифр в базе 2^30, что позволяет Python поддерживать сверхдлинные целые числа . Поскольку нет явного ограничения на размер, работа с целыми числами в Python чрезвычайно удобна, поскольку мы можем выполнять операции с очень длинными числами, не беспокоясь о переполнении целых чисел. Это удобство достигается за счет того, что распределение является дорогостоящим, а тривиальные операции, такие как сложение, умножение, деление, неэффективны.

Каждое целое число в python реализовано в виде структуры C, показанной ниже.

struct _longobject {
    ...
    Py_ssize_t    ob_refcnt;      // <--- holds reference count
    ...
    Py_ssize_t    ob_size;        // <--- holds number of digits
    digit         ob_digit[1];    // <--- holds the digits in base 2^30
};

Замечено, что меньшие целые числа в диапазоне от -5 до 256 используются очень часто по сравнению с другими более длинными целыми числами, и, следовательно, для повышения производительности Python предварительно выделяет этот диапазон целых чисел во время инициализации и делает их одноэлементными, и, следовательно, каждый раз, когда ссылаются на меньшее целочисленное значение, вместо выделения нового целого числа он передает ссылку на соответствующий одноэлемент.

Вот что Официальная документация Python говорит об этом предварительном распределении

Текущая реализация сохраняет массив целочисленных объектов для всех целых чисел в диапазоне от -5 до 256.Когда вы создаете int в этом диапазоне, вы фактически просто возвращаете ссылку на существующий объект.

В исходном коде CPython эта оптимизация может быть прослежена в макросе IS_SMALL_INT и функции get_small_int в longobject.c . Таким образом, python экономит много места и вычислений для часто используемых целых чисел.

Для реализации CPython встроенная функция id возвращает адрес объекта в памяти. Это означает, что если меньшие целые числа действительно являются одноэлементными, то функция id должна возвращать один и тот же адрес памяти для двух экземпляров одного и того же значения, в то время как несколько экземпляров больших значений должны возвращать разные, и это действительно то, что мы наблюдаем

>>> x, y = 36, 36
>>> id(x) == id(y)
True


>>> x, y = 257, 257
>>> id(x) == id(y)
False

Синглеты также можно увидеть в действии во время вычислений. В приведенном ниже примере мы достигаем того же целевого значения 6 выполнив две операции с тремя разными числами, 2, 4 и 10, мы увидим, что функция id возвращает одну и ту же ссылку на память в обоих случаях.

>>> a, b, c = 2, 4, 10
>>> x = a + b
>>> y = c - b
>>> id(x) == id(y)
True

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

Количество ссылок

Счетчик ссылок содержит количество различных мест, в которых есть ссылка на объект. Каждый раз , когда на объект ссылаются, ob_refcnt в его структуре увеличивается на 1 , и при разыменовании счет уменьшается на 1 . Когда счетчик ссылок становится 0 объект-это собранный мусор.

Чтобы получить текущее количество ссылок на объект, мы используем функцию getrefcount из модуля sys .

>>> ref_count = sys.getrefcount(50)
11

Когда мы делаем это для всех целых чисел в диапазоне от -5 до 300, мы получаем следующее распределение

Количество ссылок на целочисленные значения

Приведенный выше график показывает, что количество ссылок на меньшие целочисленные значения велико, что указывает на интенсивное использование, и оно уменьшается по мере увеличения значения, что подтверждает тот факт, что существует много объектов, ссылающихся на меньшие целочисленные значения по сравнению с большими во время инициализации python.

Значение 0 на него ссылаются больше всего – 359 в то время как вдоль длинного хвоста мы видим всплески количества ссылок в степенях 2, т. е. 32, 64, 128 и 256. Python во время своей инициализации сам требует небольших целочисленных значений и, следовательно, создавая синглеты, он экономит около 1993 распределения.

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

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

Другие статьи, которые вам могут понравиться

Эта статья была первоначально опубликована на моем блог – Python Кэширует Целые числа .

Если вам понравилось то, что вы прочитали, подпишитесь на мою рассылку, получите сообщение прямо в свой почтовый ящик и дайте мне знать @arpit_bhayani .

Подпишитесь на рассылку новостей подмышек