Bu eğitim Python'un önemli konularından biri olan GIL'e odaklanacaktır. Ayrıca GIL'in Python programlarının performansını kod uygulamasıyla nasıl etkilediğini de ele alacağız. Bu konuya dalmadan önce GIL hakkında temel bir fikir sahibi olalım.
GIL veya Global Tercüman Kilidi
Python Global Interpreter Lock veya GIL, çok iş parçacıklı programlamanın önemli bir parçasıdır. Birden fazla işlemle çalışırken kullanılan bir işlem kilidi türüdür. Kontrolü yalnızca bir iş parçacığına verir. Python genellikle tek bir işlemi çalıştırmak için tek bir iş parçacığı kullanır. GIL'i kullanarak tek iş parçacıklı ve çok iş parçacıklı işlemlerde aynı performans sonucunu elde ediyoruz. Python'da çoklu iş parçacığının elde edilmesini kısıtlar çünkü iş parçacıklarını engeller ve tek bir iş parçacığı olarak çalışır.
Not - Python çoklu iş parçacığını desteklemiyor çünkü iş parçacığı paketleri birden fazla CPU çekirdeğini kullanmamıza izin vermiyor.
Python Geliştiricileri Neden GIL Kullanıyor?
Python, bellek yönetimi için kullanılan benzersiz referans sayacı özelliğini sağlar. Referans sayacı, bir veri nesnesine değer atamak için Python'da dahili olarak yapılan referansların toplam sayısını sayar. Referans sayıları sıfıra ulaştığında nesnenin atanan belleği serbest bırakılır. Aşağıdaki örneği görelim.
Örnek -
import sys a = [] b = a sys.getrefcount(a)
Referans sayısı değişkeniyle ilgili temel endişe, iki veya üç iş parçacığının değerini aynı anda artırmaya veya azaltmaya çalışmasından etkilenebilmesidir. Yarış koşulu olarak bilinir. Bu durum ortaya çıkarsa, hiçbir zaman serbest bırakılmayan belleğin sızdırılmasına neden olabilir. Python programında çökebilir veya hatalar oluşabilir.
GIL, iş parçacıkları arasındaki tüm paylaşılan veri yapılarına yönelik kilitleri kullanarak bu tür bir durumu ortadan kaldırmamıza yardımcı olur, böylece tutarsız bir şekilde değiştirilmezler. Python, iş parçacığı güvenli bellek yönetimiyle ilgilendiğinden GIL'i uygulamanın kolay bir yolunu sağlar. GIL, Python'da işlenmek üzere bir iş parçacığına tek bir kilit sunmayı gerektirir. Yalnızca bir kilidin işlenmesi gerektiğinden, tek iş parçacıklı bir programın performansını artırır. Ayrıca CPU'ya bağlı herhangi bir programın oluşturulmasına yardımcı olur ve kilitlenme durumunu önler.
Çok İş parçacıklı Python Programlarına Etkisi
Performanslarındaki CPU sınırları ile tipik bir Python programı veya herhangi bir bilgisayar programı için G/Ç sınırları arasında bir fark vardır. CPU'ya bağlı programlar genellikle CPU'nun sınırlarını zorlar. Bu programlar genellikle matris çarpımları, tarama, görüntü işleme vb. gibi matematiksel hesaplamalar için kullanılır.
G/Ç bağlantılı programlar, kullanıcı, dosya, veritabanı, ağ vb. tarafından oluşturulabilen girdi/çıktıyı almak için zaman harcayan programlardır. Bu tür programların, kaynak girişi sağlayana kadar önemli miktarda süre beklemesi gerekir. Öte yandan kaynağın da kendi işlem süresi vardır. Örneğin, bir kullanıcı girdi olarak ne gireceğini düşünüyor.
Aşağıdaki örneği anlayalım.
Örnek -
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)
Çıktı:
Time taken in seconds - 7.422671556472778
Şimdi yukarıdaki kodu iki thread'i çalıştırarak değiştiriyoruz.
Örnek - 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)
Çıktı:
Time taken in seconds - 6.90830135345459
Gördüğümüz gibi her iki kodun da tamamlanması aynı süreyi aldı. GIL, ikinci kodda CPU'ya bağlı iş parçacıklarının paralel olarak yürütülmesini engelledi.
GIL Neden Hala Kaldırılmadı?
Birçok programcının bu konuda şikayeti var ancak Python, değişiklikleri GIL'in kaldırılması kadar önemli hale getiremez. Diğer bir neden ise GIL'in şu an için gelişmemiş olmasıdır. Python 3'te değişirse bazı ciddi sorunlar yaratacaktır. GIL'i kaldırmak yerine GIL konsepti gelişebilir. Guido van Rossom'a göre -
'Yalnızca tek iş parçacıklı bir programın (ve çok iş parçacıklı ancak G/Ç bağlantılı bir programın) performansı düşmezse Py3k'ye bir dizi yamayı memnuniyetle karşılarım'.
Ayrıca GIL tarafından çözülen aynı sorunu çözen birçok yöntem mevcuttur, ancak bunların uygulanması zordur.
int ayrıştırma
Python'un GIL'si ile Nasıl Başa Çıkılır
Çoklu işlemeyi kullanmak, programın GIL'den korunmasının en uygun yoludur. Python, çalıştırılacak her işlem için çeşitli tercümanlar sunar; dolayısıyla bu senaryoda, çoklu işlemdeki her işleme tek iş parçacığı sağlanır. Aşağıdaki örneği anlayalım.
Örnek -
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)
Çıktı:
Time taken in seconds - 3.3707828521728516
İyi bir performans artışı gibi görünebilir ancak süreç yönetiminin kendi yükleri vardır ve birden fazla süreç, birden fazla iş parçacığından daha ağırdır.
Çözüm
Bu eğitimde GIL'i ve onu nasıl kullanabileceğimizi tartıştık. Denetimi tek iş parçacığının aynı anda yürütülmesini sağlar. Bu eğitimde ayrıca GIL'in Python programcıları için neden önemli olduğu da ele alındı.