gh-116167: GIL को disable करने की अनुमति
(github.com/python)- CPython PR #116338 ने free-threaded build में
PYTHON_GIL=0या-X gil=0के जरिए GIL को disable करने वाला बदलावpython:mainमें merge किया - runtime में GIL को फिर से enable करने की संभावना बनाए रखने के लिए GIL-संबंधित data structures को सामान्य तरीके से initialize किया जाता है, और startup पर एक flag सेट करके
take_gil()औरdrop_gil()को जल्दी return कराया जाता है - शुरुआती जांच में
PYTHON_GIL=0सेटिंग के साथ thread का उपयोग न करने वाले कुछ tests और छोटे programs सामान्य रूप से चले, और बहुत basic thread programs कभी-कभी चले, लेकिन पूरा test suitetest_asyncioमें जल्दी crash हो गया - review के दौरान
PYTHON_GILtests, documentation,-X giloption, औरsys.flagsमें इसका reflection जोड़ा गया, औरPYTHON_GIL=1के जरिए GIL enable को force करने के लिए setting handling भी ठीक की गई - आगे के काम को incompatible extensions load करते समय GIL को फिर से enable करने और GIL को default रूप से disable करने वाले मुद्दों में अलग किया गया, और यह बदलाव Python 3.13 के free-threaded build में GIL control surface जोड़ता है
merge किया गया बदलाव
- CPython PR #116338,
gh-116167: Allow disabling the GIL with PYTHON_GIL=0 or -X gil=0बदलाव को कवर करता है colesburyने इसे 11 मार्च 2024 कोpython:mainमें merge किया- बदलाव का आकार 12 files, 163 lines added, 1 line removed के रूप में दिखाया गया
- target feature सामान्य build के लिए नहीं, बल्कि free-threaded build में GIL को disable करने वाला execution option है
GIL disable करने का तरीका
- free-threaded build में नीचे दी गई settings से GIL को disable किया जा सकता है
PYTHON_GIL=0-X gil=0
- runtime में GIL को फिर से enable किया जा सके, इसके लिए सभी GIL-संबंधित data structures को सामान्य रूप से initialize किया जाता है
- असली disable startup पर flag सेट करके किया जाता है
- इसी flag की वजह से
take_gil()औरdrop_gil()जल्दी return करते हैं
- इसी flag की वजह से
- review के दौरान
PYTHON_GIL=1होने परenable_gilको सही तरह सेट करने वाला commit भी जोड़ा गया
tests और मौजूदा सीमाएँ
PYTHON_GIL=0setting के साथ कुछ tests और छोटे programs की जांच की गई- जो tests और छोटे programs threads का उपयोग नहीं करते, वे सामान्य रूप से चलते पाए गए
- बहुत basic thread programs कभी-कभी चले
- पूरा test suite जल्दी crash हो गया, और स्थान
test_asyncioके रूप में दर्ज किया गया !buildbot nogilcommand से NoGIL-संबंधित builder tests कई बार schedule किए गए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
review के दौरान जोड़ी गई अतिरिक्त scope
corona10ने सुझाव दिया किLib/test/test_cmd_line.pyमें environment variable test जोड़ना उपयोगी होगा- इसके बाद नीचे दिए गए commits जोड़े गए
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का मानना था कि environment variable जोड़ते समय documentation भी करना अच्छा होगा- इसका आधार यह था कि
--disable-gilconfigure flag पहले से documented है - documentation में यह शामिल होना चाहिए कि यह सिर्फ free-threaded build में उपलब्ध है,
0GIL disable को force करता है,1GIL enable को force करता है, और यह Python 3.13 में नया है
- इसका आधार यह था कि
- इसके बाद
Document PYTHON_GIL environment variablecommit जोड़ा गया
-X gil option जोड़ना और अंतिम merge
- Discord चर्चा के बाद environment variable के साथ उपयोग करने के लिए
-Xoption भी जोड़ने का फैसला किया गया - PR title को केवल
PYTHON_GIL=0तक सीमित रूप से बदलकरPYTHON_GIL=0 or -X gil=0तक शामिल किया गया - अतिरिक्त commits में नीचे की बातें शामिल थीं
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,colesburyने बदलाव को approve किया- merge commit
2731913है, और merge के बादvstinnerने इस बदलाव पर प्रतिक्रिया दी: “दिलचस्प और बहुत डरावना”
आगे का काम
- follow-up issues के रूप में दो काम अलग किए गए
- मौजूदा PR GIL default बदलने के लिए नहीं है, बल्कि free-threaded build में user को environment variable या
-Xoption के जरिए GIL की स्थिति control करने देने वाला बदलाव है
1 टिप्पणियां
Hacker News की राय
no-GIL काम में दिलचस्पी रखने वालों के लिए कुछ अतिरिक्त लिंक छोड़ रहा/रही हूँ: [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
यह देखने की उत्सुकता है कि मूल Python को कितना और तेज़ बनाया जा सकता है। इस समस्या को कम करने की कोशिश करने वाले टूल बहुत ज़्यादा हो गए हैं, जिससे Python का value proposition भी चुनौती में पड़ रहा है
speed सुधारने वाले टूल के तौर पर Mojo, pytorch, triton, numba, taichi याद आते हैं। इस समस्या को हल करने की इतनी कोशिशें हैं कि पिछली बार जब एक इस्तेमाल करके देखना चाहा, तो विकल्पों की भरमार से overwhelmed हो गया/गई। आखिर में taichi चुना, और वह काफ़ी मज़ेदार और इस्तेमाल में आसान था, लेकिन उसका scope थोड़ा सीमित था
Taichi सच में कम आंका गया है। यह Metal सहित सभी platforms पर चलता है, इसके बहुत उदाहरण हैं, और code लिखना भी आसान है। सबसे बढ़कर, यह ecosystem के साथ integrate होता है और मौजूदा ecosystem को replace नहीं करता
https://github.com/taichi-dev
Taichi से क्या किया जा सकता है यह दिखाने वाला बेहतरीन demo video: 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/ में बताए गए biased reference counting तरीके में सिर्फ single-thread affinity क्यों है, और किसी दूसरे thread से access होने पर atomic increment/decrement क्यों चाहिए, यह समझना चाहता/चाहती हूँ
दूसरे implementations में, जैसे biased reference counting लागू करने वाली कई Rust crates में, मैंने यह तरीका देखा है कि नए thread में ले जाते समय ही atomic increment किया जाता है, और वह thread फिर 0 तक पहुँचने तक non-atomic increment/decrement करता है, अंत में atomic decrement करता है। क्या इसकी वजह यह है कि यह मौजूदा system पर जोड़ा जा रहा है, इसलिए एक single PyObject है और उसे नए thread-local object की तरफ point कराने के लिए replace नहीं किया जा सकता?
Rust में ownership transfer के लिए "move" language का हिस्सा है, लेकिन C या Python में इसके बराबर कोई concept नहीं है, इसलिए यह तय करना मुश्किल है कि ownership कब transfer करनी चाहिए और कौन सा thread नया owner होना चाहिए। heuristics इस्तेमाल किए जा सकते हैं। उदाहरण के लिए, किसी object को queue.SimpleQueue में डालते समय ownership छोड़ी या transfer की जा सकती है, लेकिन तब भी पहले से यह जानना मुश्किल है कि queue में पड़े object को कौन सा thread "get" करेगा
performance benefit भी शायद छोटा होगा। कई objects सिर्फ एक ही thread से access होते हैं, और कुछ objects कई threads से access होते हैं, लेकिन ऐसे objects दुर्लभ हैं जो पहले सिर्फ एक thread से exclusively access हों और बाद में सिर्फ किसी दूसरे thread से exclusively access हों
पहले tranched bread की खबर पढ़ी, और अब यह भी? कमाल का दौर है
Unladen Swallow project [1] के ठंडा पड़ जाने पर थोड़ा अफ़सोस हुआ था। Python को फिर से core optimization path पर लौटते देखकर अच्छा लग रहा है
[1] https://en.wikipedia.org/wiki/CPython#Unladen_Swallow
इसे ऐसे समझा दें जैसे मैं पाँच साल का हूँ
GIL क्या है, यह conceptually समझता/समझती हूँ। लेकिन इस बदलाव का असर क्या होगा? क्या अब overall performance improvement की उम्मीद करते हुए packages टूटने लगेंगे?
heavy CPU workload न भी हो, तब भी यह बदलाव उपयोगी हो सकता है। आजकल बहुत सा code Python की native asyncio language features से लिखा जाता है। यह NodeJS की तरह async/await के जरिए execution yield करते हुए single thread पर चलता है, और single thread से ही प्रति सेकंड हजारों requests तक का काफ़ी अच्छा throughput दे सकता है
लेकिन बड़ी समस्या यह है कि कोई भी CPU task करते ही वह बाकी सभी coroutines को block कर देता है, जिससे तरह-तरह की अस्पष्ट समस्याएँ पैदा होती हैं और requests per second खराब हो जाता है। उदाहरण के लिए, किसी एक coroutine में random I/O timeout दिख सकता है, जबकि असली कारण यह हो सकता है कि पूरी तरह अलग coroutine ने थोड़ी देर CPU पकड़े रखा। ऐसा क्यों हो रहा है, observe करना भी बहुत मुश्किल है। asyncio blocking काम को main thread से बाहर ले जाने में मदद के लिए
asyncio.to_thread()function [1] देता है, लेकिन GIL की वजह से CPU-focused काम को दूसरे coroutines में interfere करने से सच में अलग नहीं कर पाता[1] https://docs.python.org/3/library/asyncio-task.html#asyncio....
जिनके लिए जानना उपयोगी हो, GIL का मतलब Global Interpreter Lock है
यहाँ bigger picture को अच्छे से summarize करने वाला कोई resource है क्या?
आखिरकार कई tools के benchmarks का इंतज़ार है