पुस्तक ".NET प्लेटफॉर्म पर उच्च-प्रदर्शन कोड। दूसरा संस्करण

छवि यह पुस्तक आपको सिखाएगी कि प्रबंधित कोड के प्रदर्शन को अधिकतम कैसे किया जाए, आदर्श रूप से .NET पर्यावरण के किसी भी लाभ का त्याग किए बिना या सबसे खराब स्थिति में, उनमें से न्यूनतम संख्या का त्याग किए बिना। आप तर्कसंगत प्रोग्रामिंग विधियों को सीखेंगे, पता लगाएँगे कि क्या बचने के लिए और, सबसे महत्वपूर्ण, सबसे महत्वपूर्ण बात यह है कि उन उपकरणों का उपयोग कैसे करें जो उत्पादकता के स्तर को आसानी से मापने के लिए स्वतंत्र रूप से उपलब्ध हैं। प्रशिक्षण सामग्री में कम से कम पानी होगा - केवल सबसे आवश्यक। पुस्तक वही देती है जो आपको जानना आवश्यक है, यह प्रासंगिक और संक्षिप्त है, इसमें बहुत अधिक नहीं है। अधिकांश अध्याय सामान्य जानकारी और पृष्ठभूमि के साथ शुरू होते हैं, इसके बाद विशिष्ट युक्तियां, एक नुस्खा की तरह निर्धारित की जाती हैं, और अंत में चरण-दर-चरण माप और विभिन्न प्रकार के परिदृश्यों के लिए डिबगिंग अनुभाग होता है।

जिस तरह से, बेन वाटसन .NET वातावरण के विशिष्ट घटकों में डुबकी लगाएगा, विशेष रूप से, उस पर आधारित सामान्य भाषा रनटाइम (सीएलआर), और हम देखेंगे कि आपकी मशीन की मेमोरी कैसे प्रबंधित की जाती है, कोड उत्पन्न होता है, बहु-थ्रेड निष्पादन का आयोजन किया जाता है, और बहुत कुछ किया जाता है । आपको यह दिखाया जाएगा कि कैसे एक ही समय में .NET आर्किटेक्चर आपके सॉफ़्टवेयर टूल को सीमित करता है और इसे अतिरिक्त सुविधाएँ प्रदान करता है और कैसे प्रोग्रामिंग रास्तों का चुनाव आवेदन के समग्र प्रदर्शन को महत्वपूर्ण रूप से प्रभावित कर सकता है। एक बोनस के रूप में, लेखक पिछले नौ वर्षों में Microsoft पर बहुत बड़े, जटिल, उच्च-प्रदर्शन .NET सिस्टम बनाने के अनुभव से कहानियाँ आपके साथ साझा करेगा।

उद्धरण: उपयुक्त थ्रेड पूल आकार चुनें


समय के साथ, थ्रेड पूल स्वतंत्र रूप से कॉन्फ़िगर किया गया है, लेकिन बहुत शुरुआत में इसका इतिहास नहीं है और यह प्रारंभिक अवस्था में शुरू होगा। यदि आपका सॉफ़्टवेयर उत्पाद असाधारण रूप से अतुल्यकालिक है और केंद्रीय प्रोसेसर का महत्वपूर्ण रूप से उपयोग करता है, तो यह अत्यधिक उच्च प्रारंभिक लॉन्च लागतों से पीड़ित हो सकता है, और अधिक थ्रेड के निर्माण और उपलब्धता को लंबित कर सकता है। शुरुआती मापदंडों को समायोजित करना ताकि जिस क्षण से एप्लिकेशन लॉन्च किया जाता है, आपके पास अपनी उंगलियों पर कुछ निश्चित तैयार धागे होंगे:

const int MinWorkerThreads = 25; const int MinIoThreads = 25; ThreadPool.SetMinThreads(MinWorkerThreads, MinIoThreads); 

यहां सावधान रहें। टास्क ऑब्जेक्ट्स का उपयोग करते समय, उनका प्रेषण इसके लिए उपलब्ध थ्रेड्स की संख्या पर आधारित होगा। यदि उनमें से बहुत सारे हैं, तो टास्क ऑब्जेक्ट अत्यधिक शेड्यूलिंग से गुजर सकते हैं, जो कम से कम संदर्भ स्विचिंग के कारण केंद्रीय प्रोसेसर की दक्षता में कमी लाएगा। यदि कार्यभार इतना अधिक नहीं है, तो थ्रेड पूल एक एल्गोरिथ्म का उपयोग करने के लिए स्विच कर सकता है जो थ्रेड्स की संख्या को कम कर सकता है, इसे निर्दिष्ट संख्या से कम संख्या में ला सकता है।

आप SetMaxThreads विधि का उपयोग करके उनकी अधिकतम संख्या भी सेट कर सकते हैं, लेकिन यह तकनीक समान जोखिमों के अधीन है।

थ्रेड्स की आवश्यक संख्या का पता लगाने के लिए, इस पैरामीटर को अकेला छोड़ दें और थ्रेडपूल.गेटमैक्सट्रेड्स और थ्रेडपूल.जीटमाइनट्रेड्स या प्रदर्शन काउंटर का उपयोग करके स्थिर स्थिति में अपने आवेदन का विश्लेषण करें जो प्रक्रिया में शामिल थ्रेड्स की संख्या दिखाते हैं।

प्रवाह में बाधा न डालें


अन्य थ्रेड्स के काम के साथ समन्वय के बिना थ्रेड्स के काम को बाधित करना एक खतरनाक प्रक्रिया है। धाराओं को स्वयं को साफ करना चाहिए, और उन्हें एबॉर्ट विधि को कॉल करना नकारात्मक परिणामों के बिना उन्हें बंद करने की अनुमति नहीं देता है। जब कोई थ्रेड नष्ट हो जाता है, तो एप्लिकेशन के कुछ हिस्से अपरिभाषित स्थिति में होते हैं। कार्यक्रम को क्रैश करना बेहतर होगा, लेकिन आदर्श रूप से एक साफ पुनरारंभ की आवश्यकता है।

किसी थ्रेड को सुरक्षित रूप से समाप्त करने के लिए, आपको किसी प्रकार की साझा स्थिति का उपयोग करने की आवश्यकता होती है, और थ्रेड फ़ंक्शन को यह निर्धारित करने के लिए इस स्थिति की जाँच करनी चाहिए कि यह कब पूरा होना चाहिए। सामंजस्य के माध्यम से सुरक्षा प्राप्त की जानी चाहिए।

सामान्य तौर पर, आपको हमेशा टास्क ऑब्जेक्ट का उपयोग करना चाहिए - किसी कार्य को बाधित करने के लिए एक एपीआई प्रदान नहीं किया जाता है। किसी थ्रेड को लगातार समाप्त करने में सक्षम होने के लिए, आपको पहले बताए अनुसार, कैंसेलेशनटोकन टोकन का उपयोग करना चाहिए।

थ्रेड प्राथमिकता को न बदलें


सामान्य तौर पर, थ्रेड्स की प्राथमिकता को बदलना एक बेहद असफल उपक्रम है। विंडोज पर, थ्रेड प्रेषण उनकी प्राथमिकता के स्तर के अनुसार किया जाता है। यदि उच्च-प्राथमिकता वाले थ्रेड हमेशा चलाने के लिए तैयार हैं, तो कम-प्राथमिकता वाले थ्रेड्स को अनदेखा किया जाएगा और काफी कम ही उन्हें चलाने का मौका मिलेगा। एक सूत्र की प्राथमिकता को बढ़ाकर, आप कहते हैं कि इसके कार्य को अन्य प्रक्रियाओं सहित अन्य सभी कार्यों पर पूर्वता लेना चाहिए। यह एक स्थिर प्रणाली के लिए सुरक्षित नहीं है।

थ्रेड की प्राथमिकता को कम करना बेहतर है अगर यह कुछ ऐसा चल रहा है जो सामान्य प्राथमिकता के कार्यों के पूरा होने तक इंतजार कर सकता है। एक धागे की प्राथमिकता को कम करने का एक अच्छा कारण एक अनंत लूप को निष्पादित करने वाले नियंत्रण धागे की खोज करना हो सकता है। किसी थ्रेड को सुरक्षित रूप से बाधित करना असंभव है, इसलिए किसी दिए गए थ्रेड और प्रोसेसर संसाधनों को वापस करने का एकमात्र तरीका प्रक्रिया को पुनरारंभ करना है। जब तक धारा को बंद करना और इसे साफ-सफाई करना संभव नहीं हो जाता, तब तक आउट-ऑफ-कंट्रोल स्ट्रीम की प्राथमिकता कम करना परिणामों को कम करने का एक उचित तरीका होगा। यह ध्यान दिया जाना चाहिए कि कम प्राथमिकता वाले थ्रेड्स को अभी भी समय के साथ चलने की गारंटी दी जाती है: वे जितना अधिक समय तक शुरू होने से वंचित रहेंगे, उनकी डायनेमिक प्राथमिकता उतनी ही अधिक होगी। एक अपवाद निष्क्रिय प्राथमिकता THREAD_ - PRIORITY_IDLE है, जिसमें ऑपरेटिंग सिस्टम केवल एक थ्रेड को निष्पादित करने के लिए शेड्यूल करता है जब यह शाब्दिक रूप से शुरू करने के लिए अधिक नहीं होता है।

प्रवाह की प्राथमिकता में वृद्धि के लिए अच्छी तरह से उचित कारण हो सकते हैं, उदाहरण के लिए, दुर्लभ स्थितियों का तुरंत जवाब देने की आवश्यकता। लेकिन इस तरह की तकनीकों का उपयोग बहुत सतर्क होना चाहिए। विंडोज में थ्रेडिंग थ्रेड्स को उन प्रक्रियाओं की परवाह किए बिना किया जाता है जिनसे वे संबंधित हैं, इसलिए आपकी प्रक्रिया से एक उच्च प्राथमिकता वाला धागा न केवल आपके अन्य थ्रेड्स, बल्कि आपके सिस्टम पर चल रहे अन्य अनुप्रयोगों के सभी थ्रेड्स की कीमत पर लॉन्च किया जाएगा।

यदि थ्रेड पूल का उपयोग किया जाता है, तो हर बार थ्रेड के पूल में लौटने पर किसी भी प्राथमिकता में परिवर्तन को छोड़ दिया जाता है। यदि आप टास्क पैरेलल लाइब्रेरी का उपयोग करते समय बुनियादी थ्रेड्स का प्रबंधन करना जारी रखते हैं, तो आपको यह ध्यान रखना चाहिए कि पूल में वापस आने से पहले एक ही थ्रेड में कई कार्य लॉन्च किए जा सकते हैं।

थ्रेड सिंक्रोनाइज़ेशन और ब्लॉकिंग


जैसे ही वार्तालाप कई थ्रेड्स पर आता है, उन्हें सिंक्रनाइज़ करना आवश्यक हो जाता है। सिंक्रनाइज़ेशन में एक साझा राज्य तक केवल एक थ्रेड की पहुंच प्रदान करना शामिल है, उदाहरण के लिए, एक वर्ग क्षेत्र के लिए। आमतौर पर, थ्रेड्स को सिंक्रनाइज़ेशन ऑब्जेक्ट्स जैसे कि मॉनिटर, सेमीफोर, मैनुअलएसेटइवेंट, आदि का उपयोग करके सिंक्रनाइज़ किया जाता है। कभी-कभी उन्हें अनौपचारिक रूप से ताले कहा जाता है, और एक विशिष्ट थ्रेड में सिंक्रनाइज़ेशन प्रक्रिया को लॉक कहा जाता है।

तालों के बारे में बुनियादी सत्य यह है: वे कभी भी प्रदर्शन में वृद्धि नहीं करते हैं। सबसे अच्छी स्थिति में - एक अच्छी तरह से कार्यान्वित तुल्यकालन के साथ आदिम और कोई प्रतिस्पर्धा नहीं - अवरुद्ध तटस्थ हो सकता है। यह अन्य थ्रेड्स द्वारा उपयोगी कार्य के निष्पादन को रोकने की ओर जाता है और इस तथ्य से कि सीपीयू समय बर्बाद होता है, संदर्भ स्विचिंग समय बढ़ाता है और अन्य नकारात्मक परिणामों का कारण बनता है। आपको इसके साथ जुड़ना होगा क्योंकि सरल प्रदर्शन की तुलना में शुद्धता बहुत महत्वपूर्ण है। क्या गलत परिणाम की गणना जल्दी होती है कोई फर्क नहीं पड़ता!

इससे पहले कि आप लॉक तंत्र का उपयोग करने की समस्या को हल करना शुरू करें, हम सबसे बुनियादी सिद्धांतों पर विचार करेंगे।

क्या मुझे प्रदर्शन के बारे में परवाह करने की आवश्यकता है?


पहले उत्पादकता बढ़ाने की आवश्यकता को उचित ठहराएं। यह हमें अध्याय 1 में चर्चा किए गए सिद्धांतों पर वापस लाता है। प्रदर्शन आपके सभी एप्लिकेशन कोड के लिए समान रूप से महत्वपूर्ण नहीं है। सभी कोड को n-th डिग्री ऑप्टिमाइज़ेशन से गुजरना होगा। एक नियम के रूप में, यह सब "इनर लूप" से शुरू होता है - वह कोड जो निष्पादन के लिए सबसे अधिक बार या सबसे महत्वपूर्ण निष्पादित होता है - और सभी दिशाओं में फैलता है जब तक कि लागत लाभ से अधिक न हो। कोड में कई क्षेत्र हैं जो प्रदर्शन के मामले में बहुत कम महत्वपूर्ण हैं। ऐसी स्थिति में, यदि आपको लॉक की आवश्यकता है, तो शांति से इसे लागू करें।

और अब आपको सावधान रहना चाहिए। यदि आपके गैर-महत्वपूर्ण टुकड़े को थ्रेड पूल से एक थ्रेड में निष्पादित किया जाता है और आप इसे लंबे समय तक रोकते हैं, तो थ्रेड पूल अन्य अनुरोधों को संभालने के लिए अधिक थ्रेड सम्मिलित करना शुरू कर सकता है। यदि एक या दो धागे समय-समय पर ऐसा करते हैं, तो यह ठीक है। लेकिन अगर बहुत सारे धागे ऐसी चीजें करते हैं, तो एक समस्या उत्पन्न हो सकती है, क्योंकि इसके कारण, वास्तविक काम करने वाले संसाधनों को बेकार में खर्च किया जाता है। एक महत्वपूर्ण निरंतर भार के साथ एक कार्यक्रम शुरू करते समय अनजाने में उन हिस्सों से भी सिस्टम पर नकारात्मक प्रभाव पड़ सकता है, जिसके लिए अनावश्यक संदर्भ स्विच थ्रेड पूल के अनावश्यक स्विचिंग या अनुचित भागीदारी के कारण होता है। अन्य सभी मामलों की तरह, स्थिति का आकलन करने के लिए माप लिया जाना चाहिए।

क्या आपको वास्तव में लॉक की आवश्यकता है?


सबसे प्रभावी लॉकिंग तंत्र वह है जो नहीं है। यदि आप थ्रेड सिंक्रनाइज़ेशन की आवश्यकता को पूरी तरह से समाप्त कर सकते हैं, तो यह उच्च प्रदर्शन प्राप्त करने का सबसे अच्छा तरीका होगा। यह एक ऐसा आदर्श है जिसे हासिल करना इतना आसान नहीं है। आमतौर पर इसका मतलब यह है कि आपको यह सुनिश्चित करने की आवश्यकता है कि कोई साझा साझा स्थिति नहीं है - आपके आवेदन से गुजरने वाले प्रत्येक अनुरोध को स्वतंत्र रूप से दूसरे अनुरोध या कुछ केंद्रीकृत उत्परिवर्तनीय (रीड-राइट) डेटा से संसाधित किया जा सकता है। उच्च प्रदर्शन प्राप्त करने के लिए यह सुविधा सबसे अच्छा परिदृश्य होगी।

और फिर भी सावधान रहें। पुनर्गठन के साथ, ओवरबोर्ड जाना और कोड को एक गन्दा गड़बड़ में बदलना आसान है जिसे कोई भी नहीं, खुद सहित, समझ सकता है। जब तक उच्च उत्पादकता वास्तव में एक महत्वपूर्ण कारक नहीं है, तब तक आपको बहुत दूर नहीं जाना चाहिए और इसे अन्यथा प्राप्त नहीं किया जा सकता है। कोड को अतुल्यकालिक और स्वतंत्र में बदल दें, लेकिन यह स्पष्ट है।

यदि कई धागे सिर्फ एक चर से पढ़ते हैं (और एक धारा से इसे लिखने के कोई संकेत नहीं हैं), तो सिंक्रनाइज़ेशन की आवश्यकता नहीं है। सभी थ्रेड्स में असीमित पहुंच हो सकती है। यह स्वचालित रूप से अपरिवर्तनीय वस्तुओं जैसे स्ट्रिंग्स या अपरिवर्तनीय प्रकारों के मूल्यों पर लागू होता है, लेकिन किसी भी प्रकार की वस्तुओं पर लागू हो सकता है यदि आप कई थ्रेड्स द्वारा पढ़ने के दौरान इसके मूल्य की अपरिवर्तनीयता की गारंटी देते हैं।

यदि कई थ्रेड्स हैं जो कुछ साझा चर को लिखते हैं, तो देखें कि स्थानीय चर का उपयोग करके सिंक्रनाइज़ एक्सेस को समाप्त किया जा सकता है या नहीं। यदि आप काम के लिए एक अस्थायी प्रतिलिपि बना सकते हैं, तो सिंक्रनाइज़ेशन की आवश्यकता गायब हो जाएगी। यह विशेष रूप से बार-बार सिंक्रनाइज़ एक्सेस के लिए महत्वपूर्ण है। साझा चर को फिर से एक्सेस करने से, आपको स्थानीय चर को फिर से एक्सेस करने के लिए स्थानांतरित करने की आवश्यकता है, साझा चर के लिए एक बार पहुंच के बाद, जैसा कि कई थ्रेड द्वारा साझा किए गए संग्रह में आइटम जोड़ने के निम्नलिखित सरल उदाहरण में है।

 object syncObj = new object(); var masterList = new List<long >(); const int NumTasks = 8; Task[] tasks = new Task[NumTasks]; for (int i = 0; i < NumTasks; i++) { tasks[i] = Task.Run(()=> { for (int j = 0; j < 5000000; j++) { lock (syncObj) { masterList.Add(j); } } }); } Task.WaitAll(tasks); 

इस कोड को इस प्रकार परिवर्तित किया जा सकता है:

 object syncObj = new object(); var masterList = new List<long >(); const int NumTasks = 8; Task[] tasks = new Task[NumTasks]; for (int i = 0; i < NumTasks; i++) { tasks[i] = Task.Run(()=> { var localList = new List<long >(); for (int j = 0; j < 5000000; j++) { localList.Add(j); } lock (syncObj) { masterList.AddRange(localList); } }); } Task.WaitAll(tasks); 

मेरी मशीन पर, कोड का दूसरा संस्करण पहले की तुलना में दोगुना से अधिक चलता है।
अंततः, एक साझा साझा राज्य प्रदर्शन का एक मौलिक दुश्मन है। इसे डेटा सुरक्षा के लिए सिंक्रनाइज़ेशन की आवश्यकता होती है, जो प्रदर्शन को कम करती है। यदि आपके डिज़ाइन में ब्लॉकिंग से बचने का कम से कम अवसर है, तो आप एक आदर्श मल्टी-थ्रेडेड सिस्टम को लागू करने के करीब हैं।

सिंक ऑर्डर ऑर्डर करें


यह तय करते समय कि किसी भी प्रकार का सिंक्रनाइज़ेशन आवश्यक है, यह समझा जाना चाहिए कि उन सभी के पास समान प्रदर्शन या व्यवहार की विशेषताएं नहीं हैं। ज्यादातर स्थितियों में, आपको केवल एक लॉक का उपयोग करने की आवश्यकता होती है, और आमतौर पर यह मूल विकल्प होना चाहिए। अवरुद्ध करने के अलावा कुछ और का उपयोग, अतिरिक्त जटिलता को सही ठहराने के लिए, गहन माप की आवश्यकता होती है। सामान्य तौर पर, हम निम्नलिखित क्रम में सिंक्रनाइज़ेशन तंत्र पर विचार करते हैं।

1. लॉक / क्लास मॉनिटर - कोड की सादगी, समझदारी और प्रदर्शन का एक अच्छा संतुलन प्रदान करता है।

2. तुल्यकालन का पूर्ण अभाव। साझा किए गए परिवर्तनशील राज्यों, पुनर्गठन और अनुकूलन से छुटकारा पाएं। यह अधिक कठिन है, लेकिन अगर यह सफल हो जाता है, तो यह मूल रूप से ब्लॉकिंग का उपयोग करने से बेहतर काम करेगा (सिवाय इसके कि जब त्रुटियां की जाए या वास्तुकला को नीचा दिखाया जाए)।

3. इंटरलॉकिंग के लिए सरल इंटरलॉक किए गए तरीके - कुछ परिदृश्यों में अधिक उपयुक्त हो सकते हैं, लेकिन जैसे ही स्थिति अधिक जटिल हो जाती है, लॉक लॉक का उपयोग करने के लिए आगे बढ़ें।

और अंत में, यदि आप वास्तव में उनके उपयोग के लाभों को साबित कर सकते हैं, तो अधिक जटिल, जटिल ताले का उपयोग करें (ध्यान रखें: वे शायद ही कभी उतने उपयोगी होते हैं जितना आप उम्मीद करते हैं):

  1. अतुल्यकालिक ताले (बाद में इस अध्याय में चर्चा की जाएगी);
  2. बाकी सब।

विशिष्ट परिस्थितियाँ इनमें से कुछ तकनीकों के उपयोग को निर्देशित या बाधित कर सकती हैं। उदाहरण के लिए, कई इंटरलॉक किए गए तरीकों के संयोजन से एकल लॉक स्टेटमेंट को बेहतर बनाने की संभावना नहीं है।

»पुस्तक की अधिक जानकारी प्रकाशक की वेबसाइट पर पाई जा सकती है
» सामग्री
» अंश

फेरीवालों के लिए कूपन पर 25% की छूट - .NET

पुस्तक के पेपर संस्करण के भुगतान पर, एक इलेक्ट्रॉनिक पुस्तक ई-मेल द्वारा भेजी जाती है।

Source: https://habr.com/ru/post/hi458520/


All Articles