gh-116167: GIL’in devre dışı bırakılmasına izin verildi
(github.com/python)- CPython PR #116338, free-threaded derlemede
PYTHON_GIL=0veya-X gil=0ile GIL’i devre dışı bırakmayı mümkün kılan değişikliğipython:maindalı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()vedrop_gil()fonksiyonlarının erken dönmesiyle sağlanıyor - İlk kontrollerde
PYTHON_GIL=0ayarı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 paketitest_asyncioiçinde hızla çöktü - İnceleme sürecinde
PYTHON_GILtestleri, belgelendirme,-X gilseçeneği vesys.flagsyansıtması eklendi; ayrıcaPYTHON_GIL=1değ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=0değişikliğini ele alıyor colesbury, bunu 11 Mart 2024’tepython:maindalı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()vedrop_gil()erken dönüyor
- Bu bayrak nedeniyle
- İnceleme sırasında
PYTHON_GIL=1olduğundaenable_gildeğerini doğru ayarlayan bir commit de eklendi
Testler ve mevcut kısıtlar
PYTHON_GIL=0ayarı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_asyncioiçinde olduğu kaydedildi !buildbot nogilkomutuyla NoGIL ile ilgili builder testleri birkaç kez sıraya alındıx86-64 MacOS Intel ASAN NoGIL PRx86-64 MacOS Intel NoGIL PRARM64 MacOS M1 Refleaks NoGIL PRARM64 MacOS M1 NoGIL PRAMD64 Ubuntu NoGIL Refleaks PRAMD64 Ubuntu NoGIL PRAMD64 Windows Server 2022 NoGIL PR
İnceleme sırasında eklenen kapsam
corona10,Lib/test/test_cmd_line.pyiç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_lineSet enable_gil properly when PYTHON_GIL=1Don'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-gilconfigure bayrağının zaten belgelenmiş olmasına dayandırdı - Belgede bunun yalnızca free-threaded derlemelerde kullanılabildiği,
0değerinin GIL’i devre dışı bırakmayı zorladığı,1değerinin GIL’i etkinleştirmeyi zorladığı ve bunun Python 3.13’te yeni olduğu yer almalıydı
- Bunu,
- Ardından
Document PYTHON_GIL environment variablecommit’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
-Xseçeneği eklenmesine de karar verildi - PR başlığı, yalnızca
PYTHON_GIL=0ı kapsayan hâldenPYTHON_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 optionFix link to -X gilFix PYTHON_GIL versionchanged lineClarify test_flags in normal builds
ericsnowcurrently,erlend-aasland,corona10,colesburydeğişikliği onayladı- Birleştirme commit’i
2731913oldu ve birleştirmenin ardındanvstinner, 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ı
- 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
-Xseçeneğiyle GIL durumunu denetleyebilmesini sağlayan bir değişiklik
1 yorum
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
[0] https://peps.python.org/pep-0703/
[1] https://github.com/colesbury/nogil-3.12
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ı
Taichi gerçekten çok hafife alınıyor. Metal dahil tüm platformlarda çalışıyor, çok sayıda örneği var ve kod yazması kolay. En önemlisi de ekosistemle bütünleşiyor ve mevcut ekosistemin yerini almıyor
https://github.com/taichi-dev
Taichi ile neler yapılabileceğini gösteren harika demo videoları: https://www.youtube.com/watch?v=oXRJoQGCYFg
https://www.youtube.com/watch?v=WNh4Q7-OSJs
https://www.taichi-lang.org/
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
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ı?
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....
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