1 puan yazan GN⁺ 2024-03-12 | 1 yorum | WhatsApp'ta paylaş
  • CPython PR #116338, free-threaded derlemede PYTHON_GIL=0 veya -X gil=0 ile GIL’i devre dışı bırakmayı mümkün kılan değişikliği python:main dalına birleştirdi
  • Çalışma zamanında GIL’i yeniden açma olasılığını korumak için GIL ile ilgili veri yapıları her zamanki gibi başlatılıyor; devre dışı bırakma ise başlangıçta bir bayrak ayarlanarak take_gil() ve drop_gil() fonksiyonlarının erken dönmesiyle sağlanıyor
  • İlk kontrollerde PYTHON_GIL=0 ayarıyla iş parçacığı kullanmayan bazı testler ve küçük programlar düzgün çalıştı; çok temel iş parçacıklı programlar bazen çalıştı, ancak tüm test paketi test_asyncio içinde hızla çöktü
  • İnceleme sürecinde PYTHON_GIL testleri, belgelendirme, -X gil seçeneği ve sys.flags yansıtması eklendi; ayrıca PYTHON_GIL=1 değerinin GIL etkinleştirmeyi zorlaması için ayar işleme mantığı da düzeltildi
  • Devam çalışmaları, uyumsuz eklentiler yüklenirken GIL’in yeniden açılması ve GIL’in varsayılan olarak devre dışı bırakılması konularına ayrıldı; bu değişiklik Python 3.13’ün free-threaded derlemesine GIL denetim yüzeyi ekliyor

Birleştirilen değişiklik

  • CPython PR #116338, gh-116167: Allow disabling the GIL with PYTHON_GIL=0 or -X gil=0 değişikliğini ele alıyor
  • colesbury, bunu 11 Mart 2024’te python:main dalına birleştirdi
  • Değişiklik boyutu 12 dosya, 163 satır ekleme, 1 satır silme olarak görünüyor
  • Hedeflenen özellik, normal derlemeler değil, free-threaded derleme için GIL’i devre dışı bırakmaya yönelik bir çalıştırma seçeneği

GIL’i devre dışı bırakma yöntemi

  • free-threaded derlemede GIL şu ayarlarla devre dışı bırakılabiliyor
    • PYTHON_GIL=0
    • -X gil=0
  • Çalışma zamanında GIL yeniden açılabilsin diye tüm GIL ile ilgili veri yapıları her zamanki gibi başlatılıyor
  • Asıl devre dışı bırakma, başlangıçta bir bayrak ayarlanmasıyla yapılıyor
    • Bu bayrak nedeniyle take_gil() ve drop_gil() erken dönüyor
  • İnceleme sırasında PYTHON_GIL=1 olduğunda enable_gil değerini doğru ayarlayan bir commit de eklendi

Testler ve mevcut kısıtlar

  • PYTHON_GIL=0 ayarıyla bazı testler ve küçük programlar kontrol edildi
    • İş parçacığı kullanmayan testler ve küçük programların düzgün çalıştığı görüldü
    • Çok temel iş parçacıklı programlar bazen çalıştı
  • Tüm test paketi hızla çöktü ve bunun test_asyncio içinde olduğu kaydedildi
  • !buildbot nogil komutuyla NoGIL ile ilgili builder testleri birkaç kez sıraya alındı
    • x86-64 MacOS Intel ASAN NoGIL PR
    • x86-64 MacOS Intel NoGIL PR
    • ARM64 MacOS M1 Refleaks NoGIL PR
    • ARM64 MacOS M1 NoGIL PR
    • AMD64 Ubuntu NoGIL Refleaks PR
    • AMD64 Ubuntu NoGIL PR
    • AMD64 Windows Server 2022 NoGIL PR

İnceleme sırasında eklenen kapsam

  • corona10, Lib/test/test_cmd_line.py içine ortam değişkeni testi eklemenin değerli olacağını önerdi
  • Sonrasında şu commit’ler eklendi
    • Add test for PYTHON_GIL in test_cmd_line
    • Set enable_gil properly when PYTHON_GIL=1
    • Don't add 'enable_gil' to test_embed in normal builds
  • colesbury, ortam değişkeni eklenirken bunun belgelenmesinin de iyi olacağını düşündü
    • Bunu, --disable-gil configure bayrağının zaten belgelenmiş olmasına dayandırdı
    • Belgede bunun yalnızca free-threaded derlemelerde kullanılabildiği, 0 değerinin GIL’i devre dışı bırakmayı zorladığı, 1 değerinin GIL’i etkinleştirmeyi zorladığı ve bunun Python 3.13’te yeni olduğu yer almalıydı
  • Ardından Document PYTHON_GIL environment variable commit’i eklendi

-X gil seçeneğinin eklenmesi ve son birleştirme

  • Discord’daki tartışmanın ardından ortam değişkeniyle birlikte kullanılabilecek bir -X seçeneği eklenmesine de karar verildi
  • PR başlığı, yalnızca PYTHON_GIL=0ı kapsayan hâlden PYTHON_GIL=0 or -X gil=0ı da kapsayacak şekilde değiştirildi
  • Eklenen commit’lerde şu içerikler vardı
    • Add -X gil option, add to sys.flags, modify test to cover env var… and option
    • Fix link to -X gil
    • Fix PYTHON_GIL versionchanged line
    • Clarify test_flags in normal builds
  • ericsnowcurrently, erlend-aasland, corona10, colesbury değişikliği onayladı
  • Birleştirme commit’i 2731913 oldu ve birleştirmenin ardından vstinner, bu değişikliğe “ilginç ve çok korkutucu” diye tepki verdi

Devam çalışmaları

  • Devam eden işler kapsamında iki görev ayrıldı
    • #116322: uyumsuz eklentiler yüklenirken GIL’i yeniden etkinleştirme
    • #116329: GIL’i varsayılan olarak devre dışı bırakma
  • Mevcut PR, GIL varsayılanını değiştiren bir değişiklik değil; free-threaded derlemede kullanıcının ortam değişkeni veya -X seçeneğiyle GIL durumunu denetleyebilmesini sağlayan bir değişiklik

1 yorum

 
GN⁺ 2024-03-12
Hacker News yorumları
  • no-GIL çalışmasını merak edenler için ek bağlantılar bırakıyorum: [0], [1]
    [0] Multithreaded Python without the GIL
    https://docs.google.com/document/d/18CXhDb1ygxg-YXNBJNzfzZsD...
    [1] Github repo
    https://github.com/colesbury/nogil

  • Varsayılan Python'ın ne kadar daha hızlı hale getirilebileceğini görmek heyecan verici. Bu sorunu hafifletmeye çalışan araçların sayısı arttıkça Python'ın değer önerisi de zorlanıyor
    Hız iyileştirme araçları olarak aklıma Mojo, pytorch, triton, numba, taichi geliyor. Bu sorunu çözmeye yönelik o kadar çok girişim var ki, geçen sefer birini denemek istediğimde seçeneklerin çokluğu altında ezilmiştim. Sonunda taichi'yi seçtim; oldukça eğlenceli ve kullanımı kolaydı, ancak kullanım alanı biraz sınırlıydı

  • https://peps.python.org/pep-0703/ adresinde açıklanan biased reference counting yönteminin neden yalnızca tek iş parçacığına yakınlık bıraktığını ve başka iş parçacıklarından erişildiğinde atomik artırma/azaltma gerektirdiğini merak ediyorum
    Başka uygulamalarda, örneğin biased reference counting uygulayan çeşitli Rust crate'lerinde, yeni bir iş parçacığına taşınırken yalnızca atomik olarak artırıldığını; o iş parçacığının da yeniden 0'a ulaşana kadar atomik olmayan artırma/azaltma yaptığı ve en sonda atomik azaltma gerçekleştirdiği bir yaklaşım görmüştüm. Bunun, mevcut sisteme eklenmiş bir yapı olduğu için tek bir PyObject olması ve yeni bir thread-local nesneyi gösterecek şekilde değiştirilememesinden mi kaynaklandığını merak ediyorum

    • CPython ileride sahiplik aktarımını uygulayabilir, ama bu biraz daha zor
      Rust'ta sahiplik aktarımı için "move" dilin bir parçası; C veya Python'da ise buna karşılık gelen bir kavram yok. Bu yüzden sahipliğin ne zaman aktarılması gerektiğini ve hangi iş parçacığının yeni sahip olması gerektiğini belirlemek zor. Sezgisel yöntemler kullanılabilir. Örneğin bir nesne queue.SimpleQueue'ya konduğunda sahiplikten vazgeçilebilir veya sahiplik aktarılabilir; fakat o durumda bile kuyruğa giren nesneyi hangi iş parçacığının "get" edeceğini önceden bilmek zor
      Performans kazancı da muhtemelen küçük olur. Pek çok nesneye yalnızca tek bir iş parçacığından erişiliyor; bazı nesnelere birden fazla iş parçacığı erişiyor, ancak bir süre yalnızca bir iş parçacığı tarafından özel olarak erişilip daha sonra yalnızca başka bir iş parçacığı tarafından özel olarak erişilen nesneler nadir
  • Önce tranched bread haberini okudum, şimdi de bu mu? Harika bir çağdayız
    Unladen Swallow projesi [1] sönümlendiğinde biraz üzülmüştüm. Python'ın yeniden temel optimizasyon yoluna döndüğünü görmek güzel
    [1] https://en.wikipedia.org/wiki/CPython#Unladen_Swallow

  • Beş yaşındaki birine anlatır gibi açıklanmasını isterim
    GIL'in ne olduğunu kavramsal olarak biliyorum. Peki bu değişikliğin etkisi ne? Genel bir performans artışı beklerken paketler artık bozulacak mı?

    • Eskiden GIL yüzünden pratikte çok iş parçacıklı Python neredeyse hiç yazılmazdı. İş parçacıkları daha çok bağımsız I/O işlemlerinde bekleyebilecek birden fazla işi yürütmek için kullanılırdı; bu elbette yaygın ve kullanışlıydı, ama CPU ağırlıklı Python kodunun performansına yardımcı olmazdı
      Bu değişiklik yoğun CPU işi olmasa bile faydalı olabilir. Son dönemde çok sayıda kod Python'ın yerel asyncio dil özellikleriyle yazılıyor. Bu yapı, NodeJS gibi async/await ile çalışmayı devrederek tek iş parçacığında çalışır ve yalnızca tek iş parçacığıyla bile saniyede binlerce istek düzeyinde oldukça iyi throughput sağlayabilir
      Ancak büyük sorun şu: herhangi bir CPU işi yaptığınız anda diğer tüm coroutine'leri bloke eder; bu da her türden belirsiz soruna yol açar ve saniyedeki istek sayısını bozar. Örneğin bir coroutine'de rastgele I/O timeout'ları görülebilir, ama gerçek neden bambaşka bir coroutine'in kısa süreliğine CPU'yu işgal etmesi olabilir. Bunun neden olduğunu gözlemlemek de çok zordur. asyncio, bloklayan işleri ana iş parçacığının dışına taşımaya yardımcı olan asyncio.to_thread() fonksiyonunu [1] sağlar; fakat GIL yüzünden CPU ağırlıklı işleri diğer coroutine'lere müdahale etmeyecek şekilde gerçekten ayıramaz
      [1] https://docs.python.org/3/library/asyncio-task.html#asyncio....
    • Bir paket GIL'e bağlıysa GIL etkinleştirilir. Paketler bozulmaz
  • Merak edenler için, GIL Global Interpreter Lock'un kısaltmasıdır

  • Buradaki büyük resmi iyi özetleyen bir kaynak var mı?

  • Sonunda çeşitli araçların benchmarklarını görmek için sabırsızlanıyorum