Обход GIL

Модуль 1: Python Core
14 уровень , 10 лекция
Открыта

11.1 Global Interpreter Lock

Global Interpreter Lock (GIL) — это механизм в интерпретаторе CPython, который обеспечивает, что только один поток выполняет байт-код Python в любое время. GIL предотвращает параллельное выполнение потоков, что может негативно влиять на производительность многопоточных программ, особенно на многоядерных процессорах.

Причины существования GIL

Упрощение управления памятью: GIL упрощает управление памятью и сборку мусора, делая Python проще для реализации.

Потокобезопасность: GIL предотвращает состояния гонки, делая выполнение кода потокобезопасным без необходимости использовать блокировки.

Проблемы, вызванные GIL

Ограниченная производительность: Многопоточные программы, выполняющие вычислительно интенсивные задачи, не могут полностью использовать преимущества многоядерных процессоров из-за ограничений GIL.

Искажение производительности: Программы, которые интенсивно используют потоки для выполнения задач, могут столкнуться с ухудшением производительности из-за переключения контекста между потоками.

Сейчас существует 4 основных способа «обхода GIL»:

11.2 Использование многопроцессорности (multiprocessing)

Модуль multiprocessing позволяет создавать процессы, которые выполняются параллельно и не ограничены GIL, так как каждый процесс имеет свой собственный интерпретатор Python и память.

Пример:


import multiprocessing

def worker(num):
    print(f'Worker: {num}')
            
def main():
    processes = []
    for i in range(5):
        p = multiprocessing.Process(target=worker, args=(i,))
        processes.append(p)
        p.start()
            
    for p in processes:
        p.join()
            
main()

11.3 Асинхронное программирование

Асинхронное программирование с использованием asyncio позволяет выполнять задачи параллельно, не блокируя основной поток. Хотя это не обходит GIL в прямом смысле, оно позволяет эффективно использовать время ожидания для выполнения других задач.

Пример:


import asyncio

async def main():
    await asyncio.sleep(1)
    print('Hello')
            
asyncio.run(main())

11.4 Использование библиотек с собственным управлением потоками

Некоторые библиотеки, такие как NumPy и SciPy, написаны на C и используют собственные механизмы управления потоками, что позволяет им обходить GIL и эффективно использовать многоядерные процессоры для вычислений.

Собственно, это и есть одна из основных причин успеха Python, какой бы медленный он ни был. Все сложные вычисления переписаны на языках C/C++ и выполняются на всех ядрах процессора или даже сразу на ядрах видеокарты. А на современных видеокартах тысячи ядер.

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

11.5 Выполнение вычислений вне интерпретатора Python

Использование расширений на C/C++ или других языках, которые могут выполнять вычисления вне интерпретатора Python и затем возвращать результат. Это позволяет избежать блокировки GIL на время выполнения интенсивных вычислений.

Пример использования Cython:


# example.pyx

def compute(int n):
    cdef int i, result = 0
    for i in range(n):
        result += i
    return result

Компиляция и использование:


cythonize -i example.pyx

import example
print(example.compute(1000000))
2
Задача
Модуль 1: Python Core, 14 уровень, 10 лекция
Недоступна
Использование многопроцессорности
Использование многопроцессорности
2
Задача
Модуль 1: Python Core, 14 уровень, 10 лекция
Недоступна
Асинхронное программирование
Асинхронное программирование
1
Опрос
Асинхронность, 14 уровень, 10 лекция
Недоступен
Асинхронность
Асинхронность
Комментарии (10)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Slevin Уровень 57
14 июля 2025
Для первого задания в теле функции (которая выполняется в отдельном процессе) используйте:

pr = multiprocessing.current_process()
pr.name  # имя процесса
pr.pid   # ID процесса
вывод соберите из этого сами ☺️
Ivan Уровень 59
15 мая 2025
Пример с мультипроцессингом вываливается с огромным количеством внутренних ошибок библиотеки. chatGpt подсказал, что нужно обязательно использовать конструкцию

if __name__ == '__main__':
    main()
Japan_Dragon Уровень 32
5 марта 2025
долго сидела думала над тем, в чем реальная разница между java и python ну т.е. есть механическое разделение на этапы чтобы превратить в байт-код технически для разработчика всё равно это выглядит как нажатие RUN в среде разработки, чет с трудом представляю отдельную команду javac чтобы жали
Денис Уровень 37
18 февраля 2025
Получается, что уже не важно, насколько язык быстрый или медленный, если все ресурсоемкие вычисления выполняются внешними библиотеками или вообще вынесены в удаленные дата-центры. Вот вроде крутое утверждение, но фактически признание того что питон это не более, чем шелл для сишных либ, а не язык программирования.
UnknownReboot Уровень 30
20 сентября 2025
А Си это шелл для ассемблерных либ?
Денис Уровень 37
20 сентября 2025
Нет, Си это язык программирования общего назначения. Он компилируется в бинарники которые нативно выполняются непосредственно на конкретном железе. Питон это просто gluecode для вызова нормальных инструментов :) при том с крайне херовой производительностью, но производительность нормальных инструментов конечно решает.
UnknownReboot Уровень 30
20 сентября 2025
Важность скорости языков определяется задачами, а питон так обвешан синшными либами, потому что он полюбился учёным. А им скорость работы языка очень важна.
Денис Уровень 37
21 сентября 2025
Я бы скорее сказал, что у людей была необходимость реализовать производительный и надёжный функционал, они его и реализовали на подходящих для этого языках. Вся прелесть питона в скорости прототипирования, он всеядный и почти всепрощающий, однако такое удобство выливается в проблемы с производительностью. Вот и получается что питон это хороший шелл (оркестратор если угодно) для сшивки и запуска библиотек написанных на более производительных языках, что окупает его собственную медлительность. А вот пытаться делать производительную систему на голом питоне уже звучит как фантастика.
Александр Уровень 45
11 октября 2024
Для получения текущего идентификатор процесса можно использовать os.getpid()
Денис Уровень 45
18 октября 2024
Или multiprocessing.current_process().pid