Категории

Язык
Интерпретатор

17 января 2018 г. 12:18 (ред. 17 января 2018 г. 12:31)
О нововведении в CPython от инженеров из Instagram.
Около полугода остаётся до выпуска Pyhon 3.7, но уже сейчас по PEP, назначенным в версию, можно составить представление о том, на сколько ожидаемой она получится. Однако предположения по улучшению (PEP), это далеко не всё — в трекере языка на грядующую версию зафиксировано и разрешено множество задач, одной из которых и будет посвящена данная заметка.

Речь идёт о BPO-31558, в рамках которой был реализован новый метод gc.freeze().

Допустим у вас есть некий основной процесс, от которого ответвляются (fork) новые процессы. Эти процессы изначально используют память, выделенную для родительского, но только до момента, когда им понадобится изменить её. В этом случае задействуется механизм операционной системы, называемый «копирование-при-записи» (copy-on-write, COW), во время которого для дочернего процесса будет создана копия страниц памяти, подлежащих изменению.

Всё бы хорошо, но сборщик мусора (garbage collector, GC) в Питоне сводит на нет практически всю пользу копирования-при-записи, потому что в ходе сборки обновляет данные в объектах (счётчик ссылок), тем самым провоцируя копирование. Нетрудно догадаться, что в случае наличия большого количества дочерних процессов значительно повысится потребление памяти.

В качестве механизма, позволяющего экономить память в подобных случаях и был предложен новый метод «заморзки» — gc.freeze(). Он словно замораживает объекты, делая их недоступными для сборщика.

Если как можно раньше в жизненном цикле родительского процесса отключить сборку мусора, а потом, после разогрева приложения, вызвать заморозку, то все существующие объекты перейдут в так называемое «неизменное» (permanent) поколение. Объекты в этом поколении не учитываются при последующих сборках мусора. После этого можно вызывать ветвление процесса и снова включать сборку мусора в дочернем процессе.

Будем полагать объекты, порождённые в родительском процессе базовыми, имеющими значение и полезными для дочерних процессов. А коли так, то и произведенные нами действия вполне логичны и позволяют нам качественнее использовать копирование-при-записи.

Где такое может быть полезно? Конечно, в веб-приложениях, ветвящих процессы. А также и в любом долгоживущем приложении, ведь чем дольше оно живёт, тем больше объектов порождает и тем больше времени требуется на сборку мусора.

Помимо метода заморозки появились также:
  • разморозка gc.unfreeze() — передвигает объекты из неизменного поколения в старейшее;
  • подсчёт замороженных gc.get_freeze_count().

На заметку
Подробнее о том, как Жекун Ли и инженеры Instagram пришли к данному решению можно узнать из статьи «Copy-on-write friendly Python garbage collection».

А начинать морозить будем летом.