पाइथन ने हाल ही में बहुत अधिक ध्यान आकर्षित किया है। इस वर्ष अक्टूबर के लिए नियोजित 3.13 रिलीज, जीआईएल को हटाने का बड़ा काम शुरू करेगी। उन जिज्ञासु उपयोगकर्ताओं के लिए एक प्रीरिलीज़ पहले से ही उपलब्ध है जो (लगभग) जीआईएल-रहित पायथन को आज़माना चाहते हैं।
इस सारे प्रचार ने मुझे अपनी भाषा, आर्कस्क्रिप्ट में खोज करने के लिए प्रेरित किया, क्योंकि मेरे पास पहले भी एक ग्लोबल वीएम लॉक था (2020 में संस्करण 3.0.12 में जोड़ा गया, 2022 में 3.1.3 में हटा दिया गया), चीजों की तुलना करें और मुझे पायथन जीआईएल के कैसे और क्यों के बारे में गहराई से जानने के लिए मजबूर करें।
ग्लोबल इंटरप्रेटर लॉक (जीआईएल) एक तंत्र है जिसका उपयोग कंप्यूटर-भाषा दुभाषियों में थ्रेड के निष्पादन को सिंक्रनाइज़ करने के लिए किया जाता है ताकि केवल एक मूल थ्रेड (प्रति प्रक्रिया) बुनियादी संचालन (जैसे मेमोरी आवंटन और संदर्भ गिनती) निष्पादित कर सके। समय।
विकिपीडिया — वैश्विक दुभाषिया लॉक
Concurrency वह है जब दो या दो से अधिक कार्य ओवरलैपिंग समय अवधि में शुरू, चल सकते हैं और पूरे हो सकते हैं, लेकिन इसका मतलब यह नहीं है कि वे दोनों एक साथ चलेंगे।
समानांतरता तब होता है जब कार्य सचमुच एक ही समय में चलते हैं, उदाहरण के लिए मल्टीकोर प्रोसेसर पर।
गहराई से स्पष्टीकरण के लिए, इस स्टैक ओवरफ्लो उत्तर की जांच करें।
जीआईएल सिंगल-थ्रेडेड प्रोग्राम की गति बढ़ा सकता है क्योंकि आपको सभी डेटा संरचनाओं पर लॉक प्राप्त करने और जारी करने की आवश्यकता नहीं है: संपूर्ण दुभाषिया लॉक है इसलिए आप डिफ़ॉल्ट रूप से सुरक्षित हैं।
हालांकि, चूंकि प्रति दुभाषिया एक जीआईएल है, जो समानता को सीमित करता है: आपको एक से अधिक कोर का उपयोग करने के लिए एक अलग प्रक्रिया में (थ्रेडिंग के बजाय मल्टीप्रोसेसिंग मॉड्यूल का उपयोग करके) एक बिल्कुल नया दुभाषिया तैयार करने की आवश्यकता है! इसमें एक नया थ्रेड तैयार करने की तुलना में अधिक लागत है क्योंकि अब आपको अंतर-प्रक्रिया संचार के बारे में चिंता करनी होगी, जो एक नगण्य ओवरहेड जोड़ता है (बेंचमार्क के लिए GeekPython - GIL Python 3.13 में वैकल्पिक बनें देखें)।
पायथन के मामले में, यह मुख्य कार्यान्वयन, सीपीथॉन पर निर्भर करता है, जिसमें थ्रेड-सुरक्षित मेमोरी प्रबंधन नहीं है। जीआईएल के बिना, निम्नलिखित परिदृश्य दौड़ की स्थिति उत्पन्न करेगा:
यदि थ्रेड 1 पहले चलता है, तो गिनती 11 होगी (गिनती * 2 = 10, फिर गिनती 1 = 11)।
यदि थ्रेड 2 पहले चलता है, तो गिनती 12 होगी (गिनती 1 = 6, फिर गिनती * 2 = 12)।
निष्पादन का क्रम मायने रखता है, लेकिन इससे भी बदतर हो सकता है: यदि दोनों थ्रेड एक ही समय में गिनती पढ़ते हैं, तो एक दूसरे के परिणाम को मिटा देगा, और गिनती या तो 10 या 6 होगी!
कुल मिलाकर, GIL होने से सामान्य मामलों में (CPython) कार्यान्वयन आसान और तेज़ हो जाता है:
यह सी लाइब्रेरी को लपेटना भी आसान बनाता है, क्योंकि जीआईएल के कारण आपको थ्रेड-सुरक्षा की गारंटी मिलती है।
नकारात्मक पक्ष यह है कि आपका कोड अतुल्यकालिक है जैसा कि समवर्ती में है, लेकिन समानांतर नहीं है।
[!टिप्पणी]
पायथन 3.13 जीआईएल को हटा रहा है!पीईपी 703 ने एक बिल्डिंग कॉन्फ़िगरेशन --disable-gil जोड़ा है ताकि पायथन 3.13 स्थापित करने पर, आप मल्टीथ्रेडेड कार्यक्रमों में प्रदर्शन सुधार से लाभ उठा सकें।
पायथन में, फ़ंक्शंस को एक रंग लेना होता है: वे या तो "सामान्य" या "async" होते हैं। अभ्यास में इसका क्या मतलब है?
>>> def foo(call_me): ... print(call_me()) ... >>> async def a_bar(): ... return 5 ... >>> def bar(): ... return 6 ... >>> foo(a_bar):2: RuntimeWarning: coroutine 'a_bar' was never awaited RuntimeWarning: Enable tracemalloc to get the object allocation traceback >>> foo(bar) 6
क्योंकि एक एसिंक्रोनस फ़ंक्शन तुरंत कोई मान नहीं लौटाता है, बल्कि एक कॉरआउटिन को आमंत्रित करता है, हम उन्हें हर जगह कॉलबैक के रूप में उपयोग नहीं कर सकते हैं, जब तक कि जिस फ़ंक्शन को हम कॉल कर रहे हैं वह एसिंक कॉलबैक लेने के लिए डिज़ाइन नहीं किया गया है।
हमें फ़ंक्शंस का एक पदानुक्रम मिलता है, क्योंकि "सामान्य" फ़ंक्शंस को प्रतीक्षा कीवर्ड का उपयोग करने के लिए एसिंक्स बनाने की आवश्यकता होती है, जो एसिंक्रोनस फ़ंक्शंस को कॉल करने के लिए आवश्यक है:
can call normal -----------> normal can call async - -----------> normal | .-----------> async
कॉल करने वाले पर भरोसा करने के अलावा, यह जानने का कोई तरीका नहीं है कि कॉलबैक एसिंक है या नहीं (जब तक कि आप अपवाद की जांच करने के लिए इसे पहले प्रयास/छोड़कर ब्लॉक के अंदर कॉल करने का प्रयास न करें, लेकिन यह बदसूरत है)।
शुरुआत में, आर्कस्क्रिप्ट एक ग्लोबल वीएम लॉक (पायथन के जीआईएल के समान) का उपयोग कर रहा था, क्योंकि http.arkm मॉड्यूल (HTTP सर्वर बनाने के लिए उपयोग किया जाता था) मल्टीथ्रेडेड था और इसने संशोधित वेरिएबल्स के माध्यम से अपने राज्य को बदलकर आर्कस्क्रिप्ट के वीएम के साथ समस्याएं पैदा कीं और कई थ्रेड्स पर फ़ंक्शन को कॉल करना।
फिर 2021 में, मैंने वीएम स्थिति को संभालने के लिए एक नए मॉडल पर काम करना शुरू किया ताकि हम इसे आसानी से समानांतर कर सकें, और इसके बारे में एक लेख लिखा। इसे बाद में 2021 के अंत तक लागू किया गया और ग्लोबल वीएम लॉक हटा दिया गया।
आर्कस्क्रिप्ट एसिंक फ़ंक्शंस के लिए कोई रंग निर्दिष्ट नहीं करता है, क्योंकि वे भाषा में मौजूद नहीं हैं: आपके पास या तो एक फ़ंक्शन है या एक क्लोजर है, और दोनों एक दूसरे को बिना किसी अतिरिक्त सिंटैक्स के कॉल कर सकते हैं (क्लोजर एक खराब मैन ऑब्जेक्ट है, इस भाषा में: परिवर्तनशील स्थिति रखने वाला एक फ़ंक्शन)।
किसी भी फ़ंक्शन को कॉल साइट (घोषणा के बजाय) पर एसिंक बनाया जा सकता है:
(let foo (fun (a b c) ( a b c))) (print (foo 1 2 3)) # 6 (let future (async foo 1 2 3)) (print future) # UserType (print (await future)) # 6 (print (await future)) # nil
एसिंक बिल्टिन का उपयोग करके, हम तर्कों के एक सेट को देखते हुए अपने फ़ंक्शन को चलाने के लिए हुड के नीचे एक std::future (std::async और थ्रेड्स का लाभ उठाते हुए) उत्पन्न कर रहे हैं। फिर हम wait (एक अन्य बिल्टिन) को कॉल कर सकते हैं और जब चाहें परिणाम प्राप्त कर सकते हैं, जो फ़ंक्शन के वापस आने तक वर्तमान VM थ्रेड को ब्लॉक कर देगा।
इस प्रकार, किसी भी फ़ंक्शन और किसी भी थ्रेड से प्रतीक्षा करना संभव है।
यह सब संभव है क्योंकि हमारे पास एक एकल वीएम है जो आर्क::इंटरनल::एक्ज़ीक्यूशनकॉन्टेक्स्ट के अंदर मौजूद स्थिति पर काम करता है, जो एक ही धागे से बंधा होता है। VM को थ्रेड्स के बीच साझा किया जाता है, संदर्भों के बीच नहीं!
.---> thread 0, context 0 | ^ VM thread 1, context 1
एसिंक का उपयोग करके भविष्य बनाते समय, हम हैं:
यह थ्रेड्स के बीच किसी भी प्रकार के सिंक्रनाइज़ेशन को प्रतिबंधित करता है क्योंकि आर्कस्क्रिप्ट संदर्भों या किसी भी प्रकार के लॉक को उजागर नहीं करता है जिसे साझा किया जा सकता है (यह सरलता कारणों से किया गया था, क्योंकि भाषा का लक्ष्य कुछ हद तक न्यूनतम लेकिन फिर भी प्रयोग करने योग्य होना है)।
हालाँकि, यह दृष्टिकोण पाइथॉन से बेहतर (न ही बदतर) है, क्योंकि हम प्रति कॉल एक नया थ्रेड बनाते हैं, और प्रति सीपीयू थ्रेड की संख्या सीमित है, जो थोड़ा महंगा है। सौभाग्य से मुझे नहीं लगता कि इससे निपटना एक समस्या है, क्योंकि किसी को कभी भी एक साथ सैकड़ों या हजारों थ्रेड नहीं बनाने चाहिए और न ही सैकड़ों या हजारों एसिंक पायथन फ़ंक्शन को एक साथ कॉल करना चाहिए: दोनों के परिणामस्वरूप आपका प्रोग्राम बहुत धीमा हो जाएगा।
पहले मामले में, यह आपकी प्रक्रिया (यहां तक कि कंप्यूटर) को धीमा कर देगा क्योंकि ओएस हर थ्रेड को समय देने के लिए संघर्ष कर रहा है; दूसरे मामले में यह पायथन का शेड्यूलर है जिसे आपके सभी कोरआउट्स के बीच तालमेल बिठाना होगा।
[!टिप्पणी]
बॉक्स से बाहर, आर्कस्क्रिप्ट थ्रेड सिंक्रोनाइज़ेशन के लिए तंत्र प्रदान नहीं करता है, लेकिन भले ही हम किसी फ़ंक्शन में UserType (जो प्रकार-मिटाए गए C ऑब्जेक्ट के शीर्ष पर एक आवरण है) पास करते हैं, अंतर्निहित ऑब्जेक्ट 'है कॉपी किया गया।
कुछ सावधानीपूर्वक कोडिंग के साथ, उपयोगकर्ता टाइप निर्माण का उपयोग करके एक लॉक बनाया जा सकता है, जो थ्रेड्स के बीच सिंक्रनाइज़ेशन की अनुमति देगा।(let lock (module:createLock)) (let foo (fun (lock i) { (lock true) (print (str:format "hello {}" i)) (lock false) })) (async foo lock 1) (async foo lock 2)
आर्कस्क्रिप्ट और पायथन दो अलग-अलग प्रकार के एसिंक/वेट का उपयोग करते हैं: पहले वाले को कॉल साइट पर एसिंक के उपयोग की आवश्यकता होती है और अपने स्वयं के संदर्भ के साथ एक नया थ्रेड उत्पन्न करता है, जबकि बाद वाले को प्रोग्रामर को फ़ंक्शन को एसिंक के रूप में चिह्नित करने की आवश्यकता होती है प्रतीक्षा का उपयोग करने में सक्षम हो, और वे एसिंक फ़ंक्शंस कोरटाइन हैं, जो दुभाषिया के समान थ्रेड में चल रहे हैं।
मूल रूप से lexp.lt से
अस्वीकरण: उपलब्ध कराए गए सभी संसाधन आंशिक रूप से इंटरनेट से हैं। यदि आपके कॉपीराइट या अन्य अधिकारों और हितों का कोई उल्लंघन होता है, तो कृपया विस्तृत कारण बताएं और कॉपीराइट या अधिकारों और हितों का प्रमाण प्रदान करें और फिर इसे ईमेल पर भेजें: [email protected] हम इसे आपके लिए यथाशीघ्र संभालेंगे।
Copyright© 2022 湘ICP备2022001581号-3