Sobes.tech
Middle

Čo je GIL (Global Interpreter Lock) v Pythone?

sobes.tech AI

Odpoveď od AI

GIL (Global Interpreter Lock) je mechanizmus u interpretatoru CPython koji omogućava da se u jednom trenutku izvršava samo jedna nit (thread). Čak i na višejedrnim procesorima, GIL ne omogućava nitima da stvarno paralelno izvršavaju Python kod.

Razlozi postojanja GIL-a:

  • Olakšavanje upravljanja memorijom: GIL pojednostavljuje implementaciju bezbednog za niti upravljanja memorijom i sakupljanja otpada (reference counting). Bez GIL-a, bili bi potrebni složeniji i resursno skuplji mehanizmi zaključavanja za zaštitu zajedničkih struktura podataka.
  • Očuvanje kompatibilnosti sa C bibliotekama: GIL olakšava integraciju sa postojećim bibliotekama u C, koje možda nisu bezbedne za niti.

Uticaj GIL-a:

  • Ograničenje paralelizma CPU-bound zadataka: Za zadatke koji intenzivno koriste procesor, GIL postaje usko grlo, jer čak i na višejedrnim sistemima, izvršavanje se ne paralelizuje između jezgara. Niti moraju da čekaju dok se GIL ne oslobodi.
  • Manji uticaj na I/O-bound zadatke: Za zadatke koji intenzivno koriste ulaz-izlaz (mrežne zahteve, rad sa fajlovima), GIL nije toliko kritičan. Tokom čekanja na I/O operacije, nit oslobađa GIL, omogućavajući drugim nitima da rade.

Načini za izbegavanje GIL-a:

  • Multiprocessing: Umesto niti, koriste se nezavisni procesi. Svaki proces ima svoj sopstveni interpreter Python i, kao posledicu, svoj GIL. Procesi mogu da se izvršavaju paralelno na različitim jezgrima.
  • Asinhrono programiranje (asyncio): Koristi model asinhronog ulaza-izlaza u jednom niti. Umesto stvaranja mnogo niti, asinhroni kod prebacuje između zadataka tokom čekanja na I/O. GIL pri tome ne utiče toliko, jer nema paralelnog izvršavanja CPU-zavisnih zadataka.
  • C/C++ biblioteke: Delovi koda napisani u C ili C++ mogu da izvršavaju operacije bez držanja GIL, oslobađajući ga tokom izvršavanja tih operacija. Popularne biblioteke kao što su NumPy i SciPy to koriste za efikasne računarske operacije.
# Primer uticaja GIL-a na CPU-bound zadatak
import threading
import time

def cpu_bound_task():
    count = 0
    for i in range(10**7):
        count += 1

# Kreiranje dva niti za izvršavanje CPU-bound zadatka
t1 = threading.Thread(target=cpu_bound_task)
t2 = threading.Thread(target=cpu_bound_task)

start_time = time.time()
t1.start()
t2.start()
t1.join()
t2.join()
end_time = time.time()

print(f"Vreme izvršavanja sa nitima (CPU-bound): {end_time - start_time:.2f} sekundi") # Vreme će biti približno dvostruko od izvršavanja jedne zadatke zbog GIL-a

# Primer I/O-bound zadatka, gde je GIL manje uočljiv
import threading
import time

def io_bound_task():
    time.sleep(1) # Immitacija čekanja na I/O

# Kreiranje dva niti za izvršavanje I/O-bound zadatka
t1 = threading.Thread(target=io_bound_task)
t2 = threading.Thread(target=io_bound_task)

start_time = time.time()
t1.start()
t2.start()
t1.join()
t2.join()
end_time = time.time()

print(f"Vreme izvršavanja sa nitima (I/O-bound): {end_time - start_time:.2f} sekundi") # Vreme će biti oko 1 sekundu, jer niti oslobađaju GIL tokom sleep-a