Python Async, अभी तक मुख्यधारा में क्यों नहीं है?
(tonybaloney.github.io)Python Async, अभी तक मुख्यधारा में क्यों नहीं है?
Python का asyncio एक शक्तिशाली टूल है, जो I/O (इनपुट/आउटपुट) काम ज़्यादा होने वाले माहौल में वेट टाइम कम करके प्रोग्राम की दक्षता को नाटकीय रूप से बढ़ा सकता है। लेकिन कई फायदों के बावजूद सभी Python डेवलपर इसका सक्रिय रूप से उपयोग नहीं करते, और इसके पीछे कुछ बुनियादी कारण हैं।
1. दिमाग़ उलझ जाता है: संज्ञानात्मक बोझ
सबसे बड़ी बाधा है जटिलता। सिंक्रोनस कोड सहज लगता है, क्योंकि हम किताब पढ़ने की तरह ऊपर से नीचे तक उसके execution flow को क्रम से फॉलो कर सकते हैं।
लेकिन asynchronous कोड अलग होता है। यह वैसा है जैसे एक रसोइया पास्ता बनाने के लिए नूडल्स उबालने के दौरान (वेट टाइम) दूसरी तरफ सॉस बनाता है, और बचा समय हो तो सब्ज़ियाँ भी काट लेता है। नतीजे में खाना जल्दी तैयार हो सकता है, लेकिन रसोइये के दिमाग़ में लगातार यह चलता रहता है कि 'नूडल्स कितने पके?', 'सॉस जल तो नहीं रही?' जैसी कई चीज़ों की स्थिति क्या है।
इसी तरह कोड का execution flow इधर-उधर शिफ्ट होता रहता है, इसलिए यह समझना मुश्किल हो जाता है कि इस समय कौन-सा काम चल रहा है और कौन-सा इंतज़ार में है। खासकर जब bug आता है, तब उसके कारण तक पहुँचने वाली debugging प्रक्रिया बहुत पेचीदा हो जाती है।
2. अलग-अलग चलने वाला ecosystem: लाइब्रेरी compatibility
Python की एक और समस्या यह है कि उसका लाइब्रेरी ecosystem 'synchronous' और 'asynchronous' में बँटा हुआ है। बहुत-सी लोकप्रिय और सुविधाजनक लाइब्रेरीज़ (जैसे standard HTTP request लाइब्रेरी requests या कई ORM) केवल synchronous तरीके से ही काम करती हैं।
अगर asynchronous कोड के अंदर गलती से synchronous लाइब्रेरी का उपयोग कर लिया जाए, तो उस synchronous कोड के चलते रहने तक asynchronous का सबसे बड़ा फायदा, यानी 'event loop', पूरी तरह रुक जाता है। यह वैसा है जैसे कई लेन बनाई हों लेकिन इस्तेमाल सिर्फ एक ही लेन का हो रहा हो, इसलिए async इस्तेमाल करने का मतलब ही खत्म हो जाता है। इस समस्या को हल करने के लिए async को support करने वाली अलग लाइब्रेरीज़ (aiohttp, asyncpg आदि) नई सीखनी और लागू करनी पड़ती हैं, जिससे learning curve और भी कठिन हो जाता है।
3. एक समय में सिर्फ एक ही: GIL (Global Interpreter Lock)
GIL (Global Interpreter Lock) Python की पुरानी सीमाओं में से एक है। यह एक ऐसी व्यवस्था है जो एक ही process के भीतर, कई thread होने पर भी, एक समय में केवल एक thread को ही चलने देती है। asyncio single thread पर काम करता है, इसलिए इसका GIL से सीधा टकराव नहीं होता, लेकिन GIL की मौजूदगी async के उपयोग का दायरा सीमित कर देती है।
asyncio I/O wait time (जैसे network response का इंतज़ार, file read का इंतज़ार आदि) का उपयोग करने के लिए optimized है। लेकिन अगर बहुत जटिल गणना वाला CPU-intensive काम async function के अंदर शामिल हो, तो वह गणना पूरी होने तक पूरा event loop रुक जाता है। इस दौरान दूसरे I/O काम कुछ भी नहीं कर पाते और बस इंतज़ार करते रहते हैं। आखिरकार, जिन कामों में असली parallel processing चाहिए, उनके लिए अब भी multiprocessing जैसी दूसरी तकनीकों का उपयोग करना पड़ता है।
4. भविष्य की उम्मीद: Python 3.14 और GIL को हटाने की दिशा
लेकिन इन सीमाओं के बारे में एक बेहद उम्मीद जगाने वाली खबर भी है। वह है Python 3.13 से experimental रूप में शुरू हुई और 3.14 में अधिक mature होने की उम्मीद वाली GIL के optional removal की दिशा।
PEP 703 नामक प्रस्ताव के माध्यम से आगे बढ़ रहे इस बदलाव का लक्ष्य यह है कि डेवलपर चाहें तो Python कोड को GIL के बिना चला सकें। अगर यह व्यवहारिक रूप लेता है, तो Python में भी कई thread कई CPU core का एक साथ उपयोग करते हुए वास्तविक multithreading संभव हो जाएगी।
asyncio के साथ इस्तेमाल होने पर यह बहुत बड़ा synergy पैदा कर सकता है। I/O काम asyncio से कुशलतापूर्वक संभाले जा सकते हैं, और ज़्यादा computation वाले CPU काम को अलग thread में भेजकर GIL की सीमा के बिना parallel ढंग से चलाना कहीं आसान हो जाएगा। यह बदलाव Python ecosystem में एक बड़ा turning point बन सकता है, और ऐसी उम्मीद है कि यह async programming को अपनाने में बाधा बनने वाली कई दीवारें तोड़ देगा।
9 टिप्पणियां
मुझे लगता है GIL का ज़िक्र थोड़ा अचानक-सा आ गया है.. GIL हट भी जाए, तब भी
अगर I/O bound और CPU bound दोनों में multi-threading इस्तेमाल करनी हो,
तो Python के बजाय कोई दूसरा विकल्प अपनाना बेहतर नहीं होगा..
वैसे asyncio को Python में गहराई से काम करने वाले लोगों के बीच काफ़ी नापसंद किया जाता है, ऐसा लगता है।
मुझे अक्सर यह राय सुनने को मिली है कि gevent को mainsteam बन जाना चाहिए था
मैं इस बात से सहमत हूँ कि मौजूदा GIL की दिशा से, "दूसरे विकल्पों" की तुलना में भी, किसी खास कमी न रहने जैसी स्थिति की उम्मीद करना मुश्किल है।
लेकिन Python के अलावा कोई दूसरा विकल्प अपनाना चाहिए, यह बात इस तर्क की ओर नहीं जानी चाहिए कि कोई समस्या ही नहीं है; बल्कि यह इस तर्क की ओर जानी चाहिए कि समस्या है, ऐसा मुझे लगता है।
asyncioकाफ़ी इस्तेमाल होता है.. और काम का है.. task cancellation के edge-triggered (level-triggered नहीं) होने की एक सीमा है, लेकिन सच कहें तो ऐसा कोड लिखना, जो task cancellation के प्रति aware हो और gracefully handle करे, बहुत आम बात नहीं है। उससे भी बड़ी समस्या यह है कि event loop task के लिए weak reference रखता है, इसलिए वह GC की वजह से गायब हो सकता है.. लेकिन structured concurrency से यह हल हो जाता है.ज़्यादातर प्रमुख i/o कामों के लिए
asyncioसपोर्ट करने वाली लाइब्रेरी ढूँढने में कोई समस्या नहीं होती..GIL? इसका इससे बहुत बड़ा संबंध नहीं है..
asyncioको CPU intensive कामों में parallelism के लिए इस्तेमाल करने का नज़रिया ही थोड़ा अजीब है.. GIL बेहतर होगा तो CPU intensive multithreading में वह उपयोगी होगा.. async का मकसद i/o bottleneck वाले हिस्सों को जितना हो सके उतनी efficiently चलाना है...खैर, निष्कर्ष.. डिज़ाइन के स्तर पर कुछ समस्याएँ ज़रूर हैं, लेकिन लक्ष्य हासिल करने के लिए इसे इस्तेमाल करने में कोई खास दिक्कत नहीं हुई, और production में इसका अच्छे से उपयोग हो रहा है.
क्या आपने कभी gc की वजह से task collect होते हुए देखा है?
बिल्कुल, मैं भी production में
.asyncioका बहुत ज़्यादा इस्तेमाल करता हूँ, लेकिन मौजूदा उपयोग अनुभव से मैं इतना संतुष्ट नहीं हूँ कि यह कह सकूँ, 'मैं इसका अच्छी तरह इस्तेमाल कर रहा हूँ।'मौजूदा
asyncioको GIL को आधार मानकर डिज़ाइन किया गया है; एक तरह से देखें तो यह GIL से बचने की रणनीति है, इसलिए GIL सीधेasyncioके साथ इंटरैक्ट नहीं करता।लेकिन
asyncioपर आधारित पूरे concurrency programming के नज़रिए से देखें, तो यह कहना कि GIL अप्रासंगिक है, मुझे कुछ वैसा लगता है जैसे कहना, 'Python है, तो न होना स्वाभाविक है.'मैं बस joblib इस्तेमाल करूँगा
Asyncio की समस्या मुश्किल asynchronous programming की जटिलता नहीं, बल्कि उसकी घटिया गुणवत्ता है। consistency और universality को फेंककर बनाई गई design Python में कोई दुर्लभ बात नहीं है, लेकिन ProactorEventLoop जैसी चीज़ में 5 साल पहले रिपोर्ट किया गया service outage पैदा करने वाला bug आज भी बाकी है.
जिन लोगों की स्थिति ऐसी है कि उन्हें इसे ज़बरदस्ती इस्तेमाल करना पड़ता है, उनके लिए ऐसे लेखों को हँसी में उड़ाना सच में आसान नहीं है।
बेशक, GIL की वजह से शुरुआत से ही मिलने वाला फायदा दूसरे environments की तुलना में कम होना एक बड़ा कारण हो सकता है.
मुझे लगता है कि यह कहना कि GIL न हो तो synergy पैदा हो सकती है, लगभग भ्रामक है. जिस धावक का एक पैर नहीं है, उसे असुविधा के बावजूद कृत्रिम पैर लगा दिया जाए, तो क्या उसे 'synergy' कहा जाएगा?