11.1 Global Interpreter Lock
Global Interpreter Lock (GIL)
to mechanizm w interpreterze CPython, który zapewnia, że tylko jeden wątek wykonuje bytecode Pythona w danym czasie. GIL
zapobiega równoległemu wykonywaniu wątków, co może negatywnie wpływać na wydajność programów wielowątkowych, szczególnie na procesorach wielordzeniowych.
Przyczyny istnienia GIL
Uproszczenie zarządzania pamięcią: GIL
upraszcza zarządzanie pamięcią i zbieranie śmieci, dzięki czemu Python jest łatwiejszy do implementacji.
Bezpieczeństwo wątków: GIL
zapobiega stanom wyścigu, czyniąc wykonywanie kodu bezpiecznym dla wątków bez konieczności używania blokad.
Problemy spowodowane GIL
Ograniczona wydajność: Programy wielowątkowe wykonujące intensywne obliczenia nie mogą w pełni wykorzystać zalet procesorów wielordzeniowych ze względu na ograniczenia GIL
.
Zniekształcenie wydajności: Programy, które intensywnie korzystają z wątków do wykonywania zadań, mogą napotkać pogorszenie wydajności z powodu przełączania kontekstu między wątkami.
Aktualnie istnieją 4 główne sposoby „ominięcia GIL”:
11.2 Wykorzystanie wieloprocesowości (multiprocessing)
Moduł multiprocessing
pozwala tworzyć procesy, które działają równolegle i nie są ograniczone przez GIL
, ponieważ każdy proces ma własny interpreter Pythona i pamięć.
Przykład:
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 Programowanie asynchroniczne
Programowanie asynchroniczne z użyciem asyncio
pozwala wykonywać zadania równolegle, nie blokując głównego wątku. Chociaż nie omija GIL
w dosłownym znaczeniu, pozwala efektywnie wykorzystać czas oczekiwania na wykonywanie innych zadań.
Przykład:
import asyncio
async def main():
await asyncio.sleep(1)
print('Hello')
asyncio.run(main())
11.4 Wykorzystanie bibliotek z własnym zarządzaniem wątkami
Niektóre biblioteki, takie jak NumPy i SciPy, są napisane w C i używają własnych mechanizmów zarządzania wątkami, co pozwala im omijać GIL i efektywnie wykorzystywać procesory wielordzeniowe do obliczeń.
To właśnie jest jedną z głównych przyczyn sukcesu Pythona, jakkolwiek by on nie był wolny. Wszystkie skomplikowane obliczenia są przepisane na języki C/C++ i wykonywane na wszystkich rdzeniach procesora, a nawet od razu na rdzeniach karty graficznej. A na współczesnych kartach graficznych są tysiące rdzeni.
Okazuje się, że nie ma znaczenia, jak szybki czy wolny jest język, jeśli wszystkie wymagające obliczenia są wykonywane przez zewnętrzne biblioteki lub całkowicie przeniesione do zdalnych centrów danych.
11.5 Wykonywanie obliczeń poza interpretem Pythona
Użycie rozszerzeń w C/C++ lub innych językach, które mogą wykonywać obliczenia poza interpretem Pythona i następnie zwracać wynik. To pozwala uniknąć blokady GIL podczas wykonywania intensywnych obliczeń.
Przykład użycia Cython:
# example.pyx
def compute(int n):
cdef int i, result = 0
for i in range(n):
result += i
return result
Kompilacja i użycie:
cythonize -i example.pyx
import example
print(example.compute(1000000))
GO TO FULL VERSION