Blog Trac Lyrics for music

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-подобные микропотоки (но смешивать их пока что нельзя).

1

fine-grained synchronization

2

поток, который предоставляется операционной системой, а не виртуальной машиной

Add post to: Delicious Reddit Slashdot Digg Technorati Google
(already: 8) Comment post

Comments

29.09.2008 20:42 koder

Современная реализация GIL-removing http://code.google.com/p/python-safethread/ — по информации на сайте 60-65% от скорости CPython при одном потоке

30.09.2008 0:51 Сергей Щетинин

Несколько важных вещей не сказал: что локи нужны для параллельного доступа к одним и тем же данным. Если бы данные не были расшарены, то локи на них не нужны. С multiprocessing можно гарантировать к каким данным доступ будет общим (и с локом через менеджер) а к каким только в отдельном процессе.

В рамках многопоточности с кучей общих данных GIL отличная оптимизация.

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

30.09.2008 10:18 Всеволод Соловьёв

Статья без комментариев — однобока и неполноценна. Спасибо за дополнение :)

30.09.2008 13:10 magician

http://www.garyrobinson.net/2008/01/python-safethre.html

30.09.2008 15:58 Всеволод Соловьёв

Достаточно интересно, но вот в чём штука — как с этой модификацией будут работать все Си-расширения? Что-то мне подсказывает, что возможны проблемы.

Нужно будет потестировать.

10.12.2009 10:04 Денис Баженов

Я считаю, что языки не умеющие утилизировать многопроцессорные машины будут терять актуальность в будущем. А это значит что или python изменится или умрет. Я думаю что он конечно же изменится. Параллельное выполнение при помощи отдельных процессов это конечно вариант, но не надолго. Полагаться на scheduler операционной системы для эффективного параллельного выполнения — опасно. Не зря даже в Windows появились fibers и SQLServer их вовсю юзает. К тому же, IPC о котором вы говорите очень дорогое, опять таки из за посредничества операционной системы.

Comment form for «Global Interpreter Lock: факты и мифы»

Required. 30 chars of fewer.

Required.

Comment post