Добавить в корзинуПозвонить
Найти в Дзене

Как проще всего избежать ограничений GIL?

Статья подготовлена для студентов курса «Разработчик Python» в образовательном проекте OTUS.
Многие из вас знают, что в Python есть GIL – Global Interpreter Lock, тот самый, который не даёт запускать несколько потоков и нагружать ядра процессора. Отчасти это так, но за GIL в Python скрывается очень много всего. И вот несколько фактов о нём: Рассмотрим один из самых очевидных способов избежать ограничений GIL’а при выполнении CPU-intensive задач. Модуль multiprocessing Запускай по процессу на ядро и вперёд! К сожалению, очевидный способ не значит, что простой. Допустим, запускается процесс, который fork’ает дочерний процесс, но перед этим загружает в память какой-нибудь большой read-only кэш, необходимый этим процессам для функционирования. Вот это класс! Казалось бы, за счёт copy-on-write (предполагаем Linux) оба процесса будут видеть один и тот же кэш, но дублировать его в памяти не придётся. В top (ну или ps) увидим, что у процессов RSS (Resident Set Size) меньше VMS (Virtual Memo
Оглавление
Статья подготовлена для студентов курса «Разработчик Python» в образовательном проекте OTUS.


Многие из вас знают, что в Python есть
GILGlobal Interpreter Lock, тот самый, который не даёт запускать несколько потоков и нагружать ядра процессора. Отчасти это так, но за GIL в Python скрывается очень много всего. И вот несколько фактов о нём:

  • GIL будет всегда. Но это не точно.
  • Захват GIL – одна из первых инструкций, которая выполняется в начале работы вашего кода.
  • Блокировка переключается каждые 100 инструкций байт-кода до версии 3.2.
  • Однако в Python до версии 3.2 у нас была возможность управлять этой цифрой. После – нет.
  • Всё потому, что в Python 3.2 и выше GIL был очень сильно переписан. Теперь блокировка переключается по времени.
  • Потоки соревнуются за захват GIL, у некоторых из них, например, тех, которые активно занимаются вводом\выводом, это получается чаще.
  • До версии 3.2 есть тонкости обработки сигналов при многопоточном коде – ваш код иногда может не получить сигнал от ОС.
  • Планированием выполнения потоков занимается ОС, а Python только говорит ей, как ему бы хотелось.
  • Вы можете обойти ограничения в своём коде при помощи multiprocessing.
  • Или написав своё Python C Extension.

Рассмотрим один из самых очевидных способов избежать ограничений GIL’а при выполнении CPU-intensive задач.

Модуль multiprocessing

Запускай по процессу на ядро и вперёд! К сожалению, очевидный способ не значит, что простой. Допустим, запускается процесс, который fork’ает дочерний процесс, но перед этим загружает в память какой-нибудь большой read-only кэш, необходимый этим процессам для функционирования.

Вот это класс! Казалось бы, за счёт copy-on-write (предполагаем Linux) оба процесса будут видеть один и тот же кэш, но дублировать его в памяти не придётся. В top (ну или ps) увидим, что у процессов RSS (Resident Set Size) меньше VMS (Virtual Memory Size). А если кто-то из них и решит туда записать, то ОС скопирует в его адресное пространство только нужные страницы.

Всё так, но есть нюансы

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

Чтобы этого избежать нужно положить кэш в shared memory, то есть разделяемую память, которая является общей для обоих процессов. Multiprocessing даёт нам такую возможность, но выбор опций достаточно скудный: можно создать одно значение или массив. Для чего-то более сложного придётся использовать mmap, но программировать это будет ещё сложнее.

Есть вопрос? Напишите в комментариях!
Станислав Ступников, разработчик рекламной платформы Mail.Ru Group и автор программы курса «Разработчик Python» в OTUS
Станислав Ступников, разработчик рекламной платформы Mail.Ru Group и автор программы курса «Разработчик Python» в OTUS

Материал подготовлен для студентов курса «Разработчик Python» в образовательном проекте OTUS. Чтобы присоединиться к ближайшей группе, обязательно пройдите вступительное тестирование:

ПРОЙТИ ТЕСТИРОВАНИЕ