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))
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ