0 added
0 removed
Original
2026-01-01
Modified
2026-03-10
1
<p>Теги: python, gil, global interpreter lock, multiprocessing, fork, read-only кэш, copy-on-write, top, ps, rss, vms, reference counting, shared memory, mmap</p>
1
<p>Теги: python, gil, global interpreter lock, multiprocessing, fork, read-only кэш, copy-on-write, top, ps, rss, vms, reference counting, shared memory, mmap</p>
2
<p>Многие из вас знают, что в Python есть<strong>GIL</strong>-<em>Global Interpreter Lock</em>, тот самый, который не даёт запускать несколько потоков и нагружать ядра процессора. Отчасти это так, но за<strong>GIL</strong>в Python скрывается очень много всего. И вот несколько фактов о нём:</p>
2
<p>Многие из вас знают, что в Python есть<strong>GIL</strong>-<em>Global Interpreter Lock</em>, тот самый, который не даёт запускать несколько потоков и нагружать ядра процессора. Отчасти это так, но за<strong>GIL</strong>в Python скрывается очень много всего. И вот несколько фактов о нём:</p>
3
<p>-<strong>GIL</strong>будет всегда. Но это не точно. - Захват<strong>GIL</strong>- одна из первых инструкций, которая выполняется в начале работы вашего кода. - Блокировка переключается каждые 100 инструкций байт-кода до версии 3.2. - Однако в Python до версии 3.2 у нас была возможность управлять этой цифрой. После - нет. - Всё потому, что в Python 3.2 и выше<strong>GIL</strong>был очень сильно переписан. Теперь блокировка переключается по времени. - Потоки соревнуются за захват<strong>GIL</strong>, у некоторых из них, например, тех, которые активно занимаются вводом\выводом, это получается чаще. - До версии 3.2 есть тонкости обработки сигналов при многопоточном коде - ваш код иногда может не получить сигнал от ОС. - Планированием выполнения потоков занимается ОС, а Python только говорит ей, как ему бы хотелось. - Вы можете обойти ограничения в своём коде при помощи multiprocessing. - Или написав своё Python C Extension.</p>
3
<p>-<strong>GIL</strong>будет всегда. Но это не точно. - Захват<strong>GIL</strong>- одна из первых инструкций, которая выполняется в начале работы вашего кода. - Блокировка переключается каждые 100 инструкций байт-кода до версии 3.2. - Однако в Python до версии 3.2 у нас была возможность управлять этой цифрой. После - нет. - Всё потому, что в Python 3.2 и выше<strong>GIL</strong>был очень сильно переписан. Теперь блокировка переключается по времени. - Потоки соревнуются за захват<strong>GIL</strong>, у некоторых из них, например, тех, которые активно занимаются вводом\выводом, это получается чаще. - До версии 3.2 есть тонкости обработки сигналов при многопоточном коде - ваш код иногда может не получить сигнал от ОС. - Планированием выполнения потоков занимается ОС, а Python только говорит ей, как ему бы хотелось. - Вы можете обойти ограничения в своём коде при помощи multiprocessing. - Или написав своё Python C Extension.</p>
4
<p>Рассмотрим один из самых очевидных способов избежать ограничений<strong>GIL</strong>’а при выполнении<strong>CPU-intensive</strong>задач.</p>
4
<p>Рассмотрим один из самых очевидных способов избежать ограничений<strong>GIL</strong>’а при выполнении<strong>CPU-intensive</strong>задач.</p>
5
<h2>Модуль multiprocessing</h2>
5
<h2>Модуль multiprocessing</h2>
6
<p>Запускай по процессу на ядро и вперёд! К сожалению, очевидный способ не значит, что простой. Допустим, запускается процесс, который<strong>fork</strong>’ает дочерний процесс, но перед этим загружает в память какой-нибудь большой<strong>read-only кэш</strong>, необходимый этим процессам для функционирования.</p>
6
<p>Запускай по процессу на ядро и вперёд! К сожалению, очевидный способ не значит, что простой. Допустим, запускается процесс, который<strong>fork</strong>’ает дочерний процесс, но перед этим загружает в память какой-нибудь большой<strong>read-only кэш</strong>, необходимый этим процессам для функционирования.</p>
7
<p>Вот это класс! Казалось бы, за счёт<strong>copy-on-write</strong>(предполагаем Linux) оба процесса будут<em>видеть</em>один и тот же кэш, но дублировать его в памяти не придётся. В<strong>top</strong>(ну или<strong>ps</strong>) увидим, что у процессов<strong>RSS</strong>(<em>Resident Set Size</em>) меньше<strong>VMS</strong>(<em>Virtual Memory Size</em>). А если кто-то из них и решит туда записать, то ОС скопирует в его адресное пространство только нужные страницы.</p>
7
<p>Вот это класс! Казалось бы, за счёт<strong>copy-on-write</strong>(предполагаем Linux) оба процесса будут<em>видеть</em>один и тот же кэш, но дублировать его в памяти не придётся. В<strong>top</strong>(ну или<strong>ps</strong>) увидим, что у процессов<strong>RSS</strong>(<em>Resident Set Size</em>) меньше<strong>VMS</strong>(<em>Virtual Memory Size</em>). А если кто-то из них и решит туда записать, то ОС скопирует в его адресное пространство только нужные страницы.</p>
8
<h2>Всё так, но есть нюансы</h2>
8
<h2>Всё так, но есть нюансы</h2>
9
<p>В Python управление памятью осуществляется с помощью<strong>reference counting</strong>. И даже простой цикл по списку, например, вызывает увеличение счётчика ссылок находящихся в нём объектов. То есть, скорее всего, наш большой кэш не останется надолго в общей памяти, а быстро скопируется в адресные пространства процессов.</p>
9
<p>В Python управление памятью осуществляется с помощью<strong>reference counting</strong>. И даже простой цикл по списку, например, вызывает увеличение счётчика ссылок находящихся в нём объектов. То есть, скорее всего, наш большой кэш не останется надолго в общей памяти, а быстро скопируется в адресные пространства процессов.</p>
10
<p>Чтобы этого избежать нужно положить кэш в<strong>shared memory</strong>, то есть разделяемую память, которая является общей для обоих процессов.<strong>Multiprocessing</strong>даёт нам такую возможность, но выбор опций достаточно скудный: можно создать одно значение или массив. Для чего-то более сложного придётся использовать<strong>mmap</strong>, но программировать это будет ещё сложнее.</p>
10
<p>Чтобы этого избежать нужно положить кэш в<strong>shared memory</strong>, то есть разделяемую память, которая является общей для обоих процессов.<strong>Multiprocessing</strong>даёт нам такую возможность, но выбор опций достаточно скудный: можно создать одно значение или массив. Для чего-то более сложного придётся использовать<strong>mmap</strong>, но программировать это будет ещё сложнее.</p>
11
<p><em>Есть вопрос? Напишите в комментариях!</em></p>
11
<p><em>Есть вопрос? Напишите в комментариях!</em></p>
12
12