Global Interpreter Lock: факты и мифы
«И вы ещё говорите о параллельном выполнении!..»
Erlang BEAM
Что такое GIL?
Global Interpreter Lock используется для того, чтобы только один поток
мог выполнять питоновский код и манипулировать данными в виртуальной
машине CPython. Эта блокировка отпускается и захватывается каждые
100 инструкций (по умолчанию, но можно изменить при помощи
sys.setcheckinterval()), а также перед участками кода, которые могут
выполняться длительное время и не модифицируют данные. Например, I/O.
Зачем он нужен?
Я просто не понимаю, что в нем может быть хорошего. Он же порождает проблемы при использовании на SMP-машинах!
Если не использовать GIL, то нужно будет использовать мелкозернистую
синхронизацию1, то есть захватывать и отпускать блокировку на
каждую модификацию какой-нибудь встроенной структуры данных (например,
при list.append()), на каждое создание или удаление указателя (так
как сборщик мусора использует счётчик ссылок). Такая попытка была
предпринята в конце девяностых Грегом Стайном, и это уменьшило
скорость интерпретатора приблизительно вдвое.
О какой многопоточности может идти речь, если интерпретатор выполняет одновременно только одну инструкцию?
Известно, что даже в практически идеальном случае от добавления второго ядра скорость вдвое не увеличивается. То есть, если в Питоне убрать GIL, а все программы модифицировать для активного использования “правильной” многопоточности, на большинстве современных компьютеров они всё равно будут работать медленнее, чем сейчас. А ведь идеальные случаи недостижимы — и действительное улучшение можно будет заметить только на четвёртом ядре, и только в небольшом количестве случаев.
Что делать?
Нужно всего лишь вспомнить, что кроме потоков существуют ещё и процессы. Просто запускать столько процессов, сколько есть ядер. Мы же с успехом пишем веб-приложения, которые можно запускать хоть на сотне серверов — почему бы это не переложить и на обычные программы? Конечно, для рядовых программ нам вряд ли понадобится такая мощь ;).
Но разные процессы веб-приложения не общаются между собой, а в большинстве обычных программ потоки между собой общаются.
Для общения между процессами нужно использовать IPC. Между прочим, в Python 2.6/3.0 появился модуль multiprocessing, который такие вещи делает совершенно элементарными, и его API очень похоже на то, которое предоставляет модуль threading.
Кстати, при использовании multiprocessing мы почти бесплатно
получаем возможность размазывать выполнение по нескольким компьютерам
(подробнее написано в документации).
При потоковой же модели распараллеливания мы таких бонусов
не получаем.
Альтернативные реализации Python
Известно, что в Stackless Python используются микропотоки, которые очень легко переключать, и всё такое. Но все эти микропотоки всё равно исполняются в одном native-потоке2, поэтому выигрыша от нескольких ядер нет.
IronPython и Jython работу с потоками полностью наследуют от CLR и JVM, поэтому у них нет глобальных блокировок. Но, одновременно с этим они наследуют и все возможные проблемы со взаимными блокировками, состояниями гонки и прочими прелестями.
В PyPy существуют две потоковых модели — через обычные ОС-потоки с глобальной блокировкой и через Stackless-подобные микропотоки (но смешивать их пока что нельзя).
Comments
Современная реализация GIL-removing http://code.google.com/p/python-safethread/ — по информации на сайте 60-65% от скорости CPython при одном потоке
Несколько важных вещей не сказал: что локи нужны для параллельного доступа к одним и тем же данным. Если бы данные не были расшарены, то локи на них не нужны. С multiprocessing можно гарантировать к каким данным доступ будет общим (и с локом через менеджер) а к каким только в отдельном процессе.
В рамках многопоточности с кучей общих данных GIL отличная оптимизация.
Вообще нужно понимать что любая операция с локом очень дорогая т.к. она обязана быть синхронной. Т.е., условно говоря, время которое один поток тратит на операции над локом вычитается из рабочего времени каждого потока. И чем больше ядер, тем дороже каждый лок.
Статья без комментариев — однобока и неполноценна. Спасибо за дополнение :)
http://www.garyrobinson.net/2008/01/python-safethre.html
Достаточно интересно, но вот в чём штука — как с этой модификацией будут работать все Си-расширения? Что-то мне подсказывает, что возможны проблемы.
Нужно будет потестировать.
Comment form for «Global Interpreter Lock: факты и мифы»