Обходной путь Python Global Interpreter Lock (GIL) в многоядерных системах, использующих набор задач в Linux? - PullRequest
20 голосов
/ 13 июня 2009

Итак, я только что закончил смотреть этот разговор о глобальной интерпретации Python (GIL) http://blip.tv/file/2232410.

Суть в том, что GIL является довольно хорошим дизайном для одноядерных систем (Python, по сути, оставляет обработку / планирование потоков вплоть до операционной системы). Но это может привести к серьезным негативным последствиям в многоядерных системах, и в результате потоки с интенсивным вводом-выводом будут сильно блокироваться потоками с интенсивным использованием ЦП, за счет переключения контекста, проблемы ctrl-C [*] и т. Д.

Так как GIL ограничивает нас в основном выполнением программы Python на одном процессоре, я думаю, почему бы не принять это и просто использовать набор задач в Linux, чтобы установить привязку программы к определенному ядру / процессору в системе (особенно в ситуация с несколькими приложениями Python, работающими в многоядерной системе)?

Итак, в конечном счете, мой вопрос таков: кто-нибудь пытался использовать набор задач в Linux с приложениями Python (особенно при запуске нескольких приложений в системе Linux, чтобы можно было использовать несколько ядер с одним или двумя приложениями Python, привязанными к конкретному ядру), и если да, каковы были результаты? это стоит делать? Это делает вещи хуже для определенных рабочих нагрузок? Я планирую сделать это и проверить его (в основном, посмотрим, займет ли программа больше или меньше времени), но хотел бы услышать от других о вашем опыте.

Дополнение: Дэвид Бизли (парень, делающий доклад в связанном видео) указал, что некоторые расширения C / C ++ вручную снимают блокировку GIL и если эти расширения оптимизированы для многоядерности (то есть для анализа научных или числовых данных / и т. Д.) .) тогда вместо того, чтобы использовать преимущества многоядерности для сокращения числа, расширение будет эффективно ограничено тем, что оно ограничено одним ядром (что потенциально может значительно замедлить вашу программу). С другой стороны, если вы не используете такие расширения, как этот

Причина, по которой я не использую многопроцессорный модуль, заключается в том, что (в этом случае) часть программы сильно связана с сетевым вводом / выводом (HTTP-запросы), поэтому наличие пула рабочих потоков - это БОЛЬШОЙ способ снизить производительность из поле, поскольку поток запускает HTTP-запрос, а затем, поскольку он ожидает ввода-вывода, отказывается от GIL, и другой поток может сделать это, так что часть программы может легко запустить более 100 потоков, не причиняя значительного вреда процессору и позволяя я на самом деле использовать пропускную способность сети, которая доступна. Что касается Python без стеков / и т.д., я не слишком заинтересован в переписывании программы или замене моего стека Python (доступность также будет проблемой).

[*] Только главный поток может получать сигналы, поэтому если вы посылаете ctrl-C, интерпретатор Python в основном пытается запустить основной поток, чтобы он мог обработать сигнал, но так как он не контролирует напрямую какой поток запускается (это оставлено для операционной системы), он в основном говорит ОС продолжать переключать потоки, пока в конечном итоге не попадет в основной поток (что, если вам не повезет, может занять некоторое время).

Ответы [ 7 ]

9 голосов
/ 13 июня 2009

Другое решение: http://docs.python.org/library/multiprocessing.html

Примечание 1. Это не ограничение языка Python, а реализация CPython.

Примечание 2: Что касается соответствия, у вашей ОС не должно быть проблем с этим.

6 голосов
/ 13 июня 2009

Я никогда не слышал, чтобы кто-нибудь использовал набор задач для повышения производительности с Python. Это не значит, что это не может произойти в вашем случае, но обязательно опубликуйте ваши результаты, чтобы другие могли критиковать ваши методы бенчмаркинга и предоставлять валидацию.

Лично я бы отделил ваши потоки ввода-вывода от потоков, связанных с процессором, используя очередь сообщений. Таким образом, ваш интерфейс теперь полностью связан с сетевым вводом-выводом (некоторые с интерфейсом HTTP, некоторые с интерфейсом очереди сообщений) и идеально подходят для вашей ситуации с потоками. Тогда процессоры с интенсивным использованием ЦП могут либо использовать многопроцессорную обработку, либо быть отдельными процессами, ожидающими работы, чтобы прибыть в очередь сообщений.

В более долгосрочной перспективе вы также можете подумать о замене внешнего интерфейса потокового ввода-вывода на Twisted или что-то вроде eventlets , потому что, даже если они не помогут производительности, они должны улучшить масштабируемость. Ваш бэкэнд теперь уже масштабируем, потому что вы можете запускать свою очередь сообщений на любом количестве машин + процессор по мере необходимости.

3 голосов
/ 21 ноября 2011

Интересным решением является эксперимент, о котором Райан Келли сообщил в своем блоге: http://www.rfk.id.au/blog/entry/a-gil-adventure-threading2/

Результаты кажутся очень удовлетворительными.
1 голос
/ 17 июня 2009

До тех пор, пока GIL не будет удален из Python, вместо потоков могут использоваться сопрограммы. У меня есть все основания полагать, что эта стратегия была реализована двумя успешными стартапами, использующими гринлеты как минимум в одном случае.

1 голос
/ 13 июня 2009

Python GIL для каждого интерпретатора Python. Это означает, что единственное, чтобы избежать проблем с ним при выполнении многопроцессорной обработки, - это просто запустить несколько интерпретаторов (т.е. использовать отдельные процессы вместо потоков для параллелизма), а затем использовать какой-либо другой примитив IPC для связи между процессами (например, сокеты). При этом GIL не является проблемой при использовании потоков с блокировкой вызовов ввода / вывода.

Основная проблема GIL, как упоминалось ранее, заключается в том, что вы не можете выполнять 2 разных потока кода Python одновременно. Блокировка потока при блокирующем вызове ввода / вывода блокируется и, следовательно, не исполняется на коде Python. Это означает, что он не блокирует GIL. Если у вас есть две задачи с интенсивным использованием ЦП в отдельных потоках Python, вот где GIL убивает многопроцессорность в Python (только реализация CPython, как указывалось ранее). Поскольку GIL останавливает ЦПУ № 1 от выполнения потока Python, в то время как ЦП № 0 занят выполнением другого потока Python.

1 голос
/ 13 июня 2009

Я нашел следующее практическое правило достаточным на протяжении многих лет: если рабочие зависят от некоторого общего состояния, я использую один многопроцессорный процесс на ядро ​​(с привязкой к процессору), а на ядро ​​- пул исправлений рабочих потоков (I / O связан). ОС позаботится о привязке различных процессов Python к ядрам.

0 голосов
/ 19 января 2012

Это довольно старый вопрос, но так как каждый раз, когда я ищу информацию, связанную с python и производительностью на многоядерных системах, этот пост всегда находится в списке результатов, я не позволю этому прошлому передо мной не поделиться своими мыслями.

Вы можете использовать многопроцессорный модуль, который вместо создания потоков для каждой задачи создает другой процесс компилятора cpython, интерпретирующий ваш код. Это позволит вашему приложению использовать преимущества многоядерных систем. Единственная проблема, которую я вижу в этом подходе, состоит в том, что у вас будут значительные накладные расходы, если вы создадите целый новый стек процессов в памяти. (http://en.wikipedia.org/wiki/Thread_(computing)#How_threads_differ_from_processes)

Модуль многопроцессорной обработки Python: http://docs.python.org/dev/library/multiprocessing.html

"Причина, по которой я не использую многопроцессорный модуль, заключается в том, что (в этом случае) часть программы сильно связана с сетевым вводом-выводом (HTTP-запросы), поэтому наличие пула рабочих потоков - БОЛЬШОЙ способ Выжимай производительность из коробки ... "

Об этом, я думаю, вы также можете иметь пул процессов: http://docs.python.org/dev/library/multiprocessing.html#using-a-pool-of-workers

Att, Leo

...