Про большой расход памяти в Python

У Python есть множество достоинств, но есть несколько недостатков, которые мешают Python стать действительно вездесущим языков. Один из таких недостатков — это большой расход памяти.

Если вы работаете на современном компьютере и не пишите приложения схожие с GTA 5, то возможно вы никогда и не задумывались, сколько памяти расходует ваша программа. Даже если вы data engineer, и вам приходится работать с большим количеством данных, возможно, вы встречались с memory error. В дальнейшим мы рассмотрим сколько расходуют памяти примитивные объекты в python.

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

Про большой расход памяти в Python

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

Рассмотрим размер основных примитивных типов данных: integer(int), float, tuple, string(str), list, dict.

основные объекты

Каков размер самого простого и часто используемого объекта int? Если вы перешли на Python с какого-либо С подобного языка, то ответите, что не более 8 байт. Сравним с python:

Я использую Python версии 3.7.0 64x.

>>> sys.getsizeof(None) 16 >>> sys.getsizeof(3) 28 >>> sys.getsizeof(9223372036854775808) 36 >>> sys.getsizeof(102946385896929697683768295837598259692) 44 >>>sys.getsizeof(98765432134567890987654321234567890987654321234567898765432123456787654) 56

В 64 битном С, int занимает 4 байта, а это гораздо меньше, чем в Python.

Числа с плавающей запятой в Python:

>>> sys.getsizeof(3.14159265359) 24

А что насчёт строковых значений?

>>> sys.getsizeof("") 49 >>> sys.getsizeof("hello world!") 61

Пустая строка в Python в 64 битной среде, занимает 49 байт!

Затем потребление памяти увеличивается за счет увеличения полезного размера значения.

Рассмотрим еще несколько не менее важных объектов:

Размер списка:

>>> sys.getsizeof([]) 64 >>> sys.getsizeof([1, "she", True]) 88

В 64-битном С размер пустого std::list() равен 16 байт и это в 4 раза меньше, чем в Python.

Рассмотрим словарь:

>>> sys.getsizeof({}) 240 >>> sys.getsizeof({"cat":123, "dog":321}) 240 >>> sys.getsizeof({"cat":123, "dog":321, "parrot":456, "pig":765, "bird":876, "fox":295}) 368

Например, пустой словарь в С занимает 48 байт.

Ну что? Мы разобрали занимаемый объем памяти самых популярных объектов в Python. Но, наверное, никто из читателей после этого сравнения не перейдет на С, я тоже не перейду. Для того, чтобы качественно писать код, с точки зрения оптимизации, мы должны иметь представление о требованиях создаваемых нами объектов. Посмотрим, как происходит выделение памяти под капотом…

внутреннее управление памятью

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

Обратимся к истории…

Возьмём любую версию python, ниже 2.1.

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

Распределитель памяти Python, называемый pymalloc, был написан Владимиром Марангозова и изначально был экспериментальной функцией Python 2.1 и 2.2, прежде чем стать включенной по умолчанию в 2.3.

В python программах используется много различных мелких объектов, которые то создаются, то уничтожаются, для этого вызываются функции malloc() (для выделения) и free() (для освобождения). Я уже упоминал, что выделение памяти ОС несколько затяжной процесс, поэтому pymalloc выделяет память куском в 256 Кб и называется это арена. Сама арена делится на пулы размер каждого 4Кб, и пулы уже делятся на блоки фиксированного размера под наши объекты.

Про большой расход памяти в Python

Если мы создаем объект, распределитель проверяет, есть ли пулы, с блоками нужного нам размера. Связанный массив usedpools хранит пулы каждого размера. И каждый пул имеет связанный список доступных блоков. Если есть нужный нам блок, то мы берем его из списка пула, эта операция очень быстрая.

Про большой расход памяти в Python

Если нет пулов, разбитых на нужные нам блоки, то нужно найти свободный пул, свободные пулы хранятся в связанном списке freepools. Если в списке есть пул, мы берем его, если нет, то придется выделить новый пул, если в Арене еще есть место, то мы отрежем новый пул используя arenabase. Если в Арене нет места, придется выделить новую Арену, вызвав malloc(). Теперь, когда у нас есть нужные блоки, забираем его с помощью usedpools.

Про большой расход памяти в Python

Когда программа удаляет объект, освобождается блок. Блок помещается в свободный список пула и если пул был полностью выделен, то он добавляется к usedpools (список пулов, имеющих свободные блоки), а если пул теперь полностью свободен, то он добавляется к freepools.

ВЫВОД:

В данной статье мы рассмотрели, как устроено распределение памяти в Python. Как вы могли заметить, Python очень прожорливый на память язык.

5
8 комментариев

Сравнение с C как эталоном это круто, но хотелось бы увидеть сравнение с Ruby и JavaScript например, чтобы хотя бы одна весовая категория была так сказать. 

И обычно целиком большой проект не пишут на чём то одном, можно же взять написать критичные узлы на C++, Go, Rust,для ускорения и снижения потребления Ram. 

Интересно было бы увидеть реализацию какой-нибудь программы на C и Python со сравнением реального потребления и насколько оно критично.

2
Ответить

Где найти нормального питонщика со знанием Джанги 3 на 200к?

Ответить
Автор

Andrew, один из вариантов воспользоваться разделом вакансии на vc.ru (https://vc.ru/job

Ответить

int в C - это не сколько угодно знаков.

Ответить

Комментарий недоступен

Ответить