logo

Co to jest GIL w Pythonie? Globalna blokada tłumacza

Ten samouczek będzie skupiał się na jednym z ważnych tematów Pythona, GIL. Omówimy także wpływ GIL na wydajność programów w Pythonie podczas implementacji kodu. Zanim zagłębimy się w ten temat, poznajmy podstawowe pojęcie o GIL.

GIL lub globalna blokada tłumacza

Python Global Interpreter Lock lub GIL jest ważną częścią programowania wielowątkowego. Jest to rodzaj blokady procesu stosowanej podczas pracy z wieloma procesami. Daje kontrolę tylko jednemu wątkowi. Ogólnie rzecz biorąc, Python używa jednego wątku do uruchomienia pojedynczego procesu. Używając GIL, uzyskujemy taki sam wynik wydajności procesów jednowątkowych i wielowątkowych. Ogranicza osiągnięcie wielowątkowości w Pythonie, ponieważ zapobiega wątkom i działa jako pojedynczy wątek.

Uwaga — Python nie obsługuje wielowątkowości, ponieważ pakiety wątków nie pozwalają nam używać wielu rdzeni procesora.

Dlaczego programiści Pythona używają GIL?

Python udostępnia unikalną funkcję licznika odwołań, która służy do zarządzania pamięcią. Licznik odwołań zlicza całkowitą liczbę odwołań wykonanych wewnętrznie w Pythonie w celu przypisania wartości do obiektu danych. Kiedy liczba referencji osiągnie zero, przypisana pamięć obiektu zostanie zwolniona. Zobaczmy poniższy przykład.

Przykład -

 import sys a = [] b = a sys.getrefcount(a) 

Głównym problemem związanym ze zmienną licznika odwołań jest to, że może mieć na nią wpływ sytuacja, gdy dwa lub trzy wątki próbują jednocześnie zwiększyć lub zmniejszyć jej wartość. Nazywa się to stanem wyścigowym. Jeśli wystąpi taki stan, może to być spowodowane wyciekiem pamięci, która nigdy nie zostanie zwolniona. Może wystąpić awaria lub błędy w programie Python.

GIL pomaga nam usunąć taką sytuację, stosując blokady wszystkich współdzielonych struktur danych w wątkach, aby nie były one niekonsekwentnie zmieniane. Python zapewnia łatwy sposób implementacji GIL, ponieważ zajmuje się zarządzaniem pamięcią bezpieczną dla wątków. GIL wymaga zaoferowania pojedynczej blokady wątku do przetwarzania w Pythonie. Zwiększa wydajność programu jednowątkowego, ponieważ wymaga obsługi tylko jednej blokady. Pomaga także w tworzeniu dowolnego programu związanego z procesorem i zapobiega zakleszczeniom.

Wpływ na wielowątkowe programy w Pythonie

Istnieje różnica między wydajnością związaną z procesorem a ograniczeniem operacji we/wy dla typowego programu w języku Python lub dowolnego programu komputerowego. Programy związane z procesorem zazwyczaj obciążają procesor do jego granic. Programy te są zwykle używane do obliczeń matematycznych, takich jak mnożenie macierzy, spalanie, przetwarzanie obrazu itp.

Programy związane z we/wy to programy, które poświęcają czas na uzyskanie danych wejściowych/wyjściowych, które mogą być wygenerowane przez użytkownika, plik, bazę danych, sieć itp. Takie programy muszą czekać przez znaczną ilość czasu, zanim źródło zapewni dane wejściowe. Z drugiej strony źródło ma również swój własny czas przetwarzania. Na przykład użytkownik zastanawia się, co wpisać jako dane wejściowe.

Rozumiemy następujący przykład.

Przykład -

 import time from threading import Thread COUNT = 100000000 def countdown(num): while num>0: num -= 1 start_time = time.time() countdown(COUNT) end_time = time.time() print('Time taken in seconds -', end_time - start_time) 

Wyjście:

 Time taken in seconds - 7.422671556472778 

Teraz modyfikujemy powyższy kod, uruchamiając dwa wątki.

Przykład - 2:

 import time from threading import Thread COUNT = 100000000 def countdown(num): while num>0: num -= 1 thread1 = Thread(target=countdown, args=(COUNT//2,)) thread2 = Thread(target=countdown, args=(COUNT//2,)) start_time = time.time() thread1.start() thread2.start() thread1.join() thread2.join() end_time = time.time() print('Time taken in seconds -', end_time - start_time) 

Wyjście:

 Time taken in seconds - 6.90830135345459 

Jak widzimy, ukończenie obu kodów zajęło tyle samo czasu. GIL uniemożliwił równoległe wykonywanie wątków związanych z procesorem w drugim kodzie.

Dlaczego GIL nie został jeszcze usunięty?

Wielu programistów skarży się na to, ale Python nie może wprowadzić zmian tak znaczących, jak usunięcie GIL. Innym powodem jest to, że GIL nie jest obecnie udoskonalany. Jeśli zmieni się to w Pythonie 3, spowoduje to poważne problemy. Zamiast usuwać GIL, można ulepszyć koncepcję GIL. Według Guido van Rossoma:

„Z radością przyjąłbym zestaw poprawek do Py3k tylko wtedy, gdy wydajność programu jednowątkowego (oraz programu wielowątkowego, ale powiązanego z we/wy) nie spadnie”.

Dostępnych jest również wiele metod rozwiązujących ten sam problem rozwiązany przez GIL, ale są one trudne do wdrożenia.

Jak radzić sobie z GIL Pythona

Korzystanie z przetwarzania wieloprocesowego jest najodpowiedniejszym sposobem zapobiegania programowi GIL. Python oferuje różne interpretery dla każdego procesu, więc w tym scenariuszu każdemu procesowi w trybie wieloprocesorowym udostępniany jest pojedynczy wątek. Rozumiemy następujący przykład.

Przykład -

 from multiprocessing import Pool import time COUNT = 50000000 def countdown(num): while num>0: num -= 1 if __name__ == '__main__': pool = Pool(processes=2) start_time = time.time() r1 = pool.apply_async(countdown, [COUNT//2]) r2 = pool.apply_async(countdown, [COUNT//2]) pool.close() pool.join() end_time = time.time() print('Time taken in seconds -', end_time - start_time) 

Wyjście:

 Time taken in seconds - 3.3707828521728516 

Może się wydawać, że wzrosła przyzwoita wydajność, ale zarządzanie procesami ma swoje własne koszty ogólne, a wiele procesów jest cięższych niż wiele wątków.

Wniosek

W tym samouczku omówiliśmy GIL i sposób, w jaki możemy go używać. Daje kontrolę pojedynczemu wątkowi do wykonania w określonym czasie. W tym samouczku omówiono także, dlaczego GIL jest ważny dla programistów Pythona.