सी ++ भाषा के लिए विशिष्ट चिंताएं
सबसे पहले, C ++ द्वारा अनिवार्य "स्टैक" या "हीप" आवंटन नहीं है । यदि आप ब्लॉक स्कोप में स्वचालित वस्तुओं के बारे में बात कर रहे हैं, तो वे "आवंटित" भी नहीं हैं। (बीटीडब्ल्यू, सी में स्वचालित भंडारण की अवधि निश्चित रूप से "आवंटित" के समान नहीं है; उत्तरार्द्ध सी समानता में "गतिशील" है।) गतिशील रूप से आवंटित स्मृति मुक्त स्टोर पर है , जरूरी नहीं कि "ढेर" पर हो। उत्तरार्द्ध अक्सर (डिफ़ॉल्ट) कार्यान्वयन होता है ।
हालांकि अमूर्त मशीन अर्थ नियमों के अनुसार, स्वचालित ऑब्जेक्ट अभी भी मेमोरी पर कब्जा कर लेते हैं, एक अनुरूप C ++ कार्यान्वयन को इस तथ्य को अनदेखा करने की अनुमति दी जाती है जब यह साबित हो सकता है कि यह कोई फर्क नहीं पड़ता (जब यह कार्यक्रम के नमूदार व्यवहार को नहीं बदलता है)। यह अनुमति ISO C ++ में नियम के अनुसार दी गई है , जो सामान्य अनुकूलन को सक्षम करने वाला सामान्य खंड भी है (और ISO C में भी लगभग एक ही नियम है)। इस नियम के अनुसार, आईएसओ सी ++ में भी नकल के नियम हैंवस्तुओं की विशिष्ट कृतियों के चूक की अनुमति देने के लिए। इसमें शामिल कंस्ट्रक्टर और डिस्ट्रक्टर कॉल को छोड़ दिया जाता है। परिणामस्वरूप, इन कोडर्स और डिस्ट्रक्टर्स में स्वचालित ऑब्जेक्ट (यदि कोई हो) को भी समाप्त कर दिया जाता है, स्रोत कोड द्वारा निहित भोले सार शब्दार्थों की तुलना में।
दूसरी ओर, नि: शुल्क स्टोर आवंटन निश्चित रूप से डिजाइन द्वारा "आवंटन" है। आईएसओ सी ++ नियमों के तहत, इस तरह के आवंटन को आवंटन फ़ंक्शन के कॉल द्वारा प्राप्त किया जा सकता है । हालाँकि, ISO C ++ 14 के बाद से, विशिष्ट मामलों में वैश्विक आवंटन फ़ंक्शन (यानी ) कॉल को मर्ज करने की अनुमति देने के लिए एक नया (गैर-अगर-अगर) नियम है ::operator new
। इसलिए डायनामिक एलोकेशन ऑपरेशंस के हिस्से भी ऑटोमैटिक ऑब्जेक्ट्स के मामले की तरह नो-ऑप हो सकते हैं।
आवंटन कार्य स्मृति के संसाधन आवंटित करते हैं। आवंटन के आधार पर वस्तुओं को आवंटन के आधार पर आगे आवंटित किया जा सकता है। स्वचालित वस्तुओं के लिए, उन्हें सीधे प्रस्तुत किया जाता है - यद्यपि अंतर्निहित मेमोरी तक पहुँचा जा सकता है और अन्य वस्तुओं को मेमोरी प्रदान करने के लिए उपयोग किया जा सकता है (प्लेसमेंट द्वारा new
), लेकिन यह मुफ्त स्टोर के रूप में बहुत मायने नहीं रखता है, क्योंकि स्थानांतरित करने का कोई तरीका नहीं है। संसाधन कहीं और।
अन्य सभी चिंताएं C ++ के दायरे से बाहर हैं। फिर भी, वे अभी भी महत्वपूर्ण हो सकते हैं।
C ++ के कार्यान्वयन के बारे में
C ++, सक्रियण रिकॉर्ड्स या प्रथम-श्रेणी के निरंतरताओं के कुछ प्रकारों (जैसे प्रसिद्ध द्वारा call/cc
) को उजागर नहीं करता है , सक्रियण रिकॉर्ड फ़्रेमों को सीधे हेरफेर करने का कोई तरीका नहीं है - जहां कार्यान्वयन को स्वचालित वस्तुओं को रखने की आवश्यकता होती है। एक बार अंतर्निहित कार्यान्वयन ("मूल" गैर-पोर्टेबल कोड, जैसे इनलाइन असेंबली कोड) के साथ कोई (गैर-पोर्टेबल) अंतर नहीं होता है, फ़्रेम के अंतर्निहित आवंटन का एक चूक काफी तुच्छ हो सकता है। उदाहरण के लिए, जब फंक्शन को इनलाइन किया जाता है, तो फ़्रेम को प्रभावी रूप से दूसरों में विलय किया जा सकता है, इसलिए यह दिखाने का कोई तरीका नहीं है कि "आवंटन" क्या है।
हालांकि, एक बार इंटरॉप्स का सम्मान किया जाता है, चीजें जटिल हो रही हैं। C ++ का एक विशिष्ट कार्यान्वयन आईएसए (निर्देश-सेट आर्किटेक्चर) पर कुछ कॉलिंग सम्मेलनों के साथ देशी (आईएसए-स्तरीय मशीन) कोड के साथ बाइनरी सीमा के रूप में इंटरोप की क्षमता को उजागर करेगा । यह स्पष्ट रूप से महंगा होगा, विशेष रूप से, स्टैक पॉइंटर को बनाए रखने के दौरान , जो अक्सर आईएसए-स्तर के रजिस्टर (सीधे विशिष्ट मशीन निर्देशों के साथ उपयोग करने के लिए) के पास होता है। स्टैक पॉइंटर (वर्तमान में सक्रिय) फ़ंक्शन कॉल के शीर्ष फ्रेम की सीमा को इंगित करता है। जब एक फ़ंक्शन कॉल दर्ज किया जाता है, तो एक नया फ्रेम आवश्यक होता है और स्टैक पॉइंटर जोड़ा जाता है या घटाया जाता है (आईएसए के सम्मेलन के आधार पर) आवश्यक फ्रेम आकार से कम नहीं। फ्रेम तब आवंटित कहा जाता हैजब ऑपरेशन के बाद स्टैक पॉइंटर। फ़ंक्शन के पैरामीटर को स्टैक फ्रेम पर भी पास किया जा सकता है, जो कॉल के लिए उपयोग किए जाने वाले कॉलिंग कन्वेंशन पर निर्भर करता है। फ़्रेम C ++ स्रोत कोड द्वारा निर्दिष्ट स्वचालित ऑब्जेक्ट्स की मेमोरी (शायद पैरामीटर सहित) पकड़ सकता है। इस तरह के कार्यान्वयन के अर्थ में, इन वस्तुओं को "आवंटित" किया जाता है। जब नियंत्रण फ़ंक्शन कॉल को बाहर निकालता है, तो फ़्रेम की आवश्यकता नहीं होती है, यह आमतौर पर कॉल से पहले स्टैक पॉइंटर को राज्य में वापस लाकर जारी किया जाता है (कॉलिंग कन्वेंशन के अनुसार पहले बचाया गया)। इसे "डीलक्लोकेशन" के रूप में देखा जा सकता है। ये ऑपरेशन सक्रियण रिकॉर्ड को प्रभावी रूप से एक LIFO डेटा संरचना बनाते हैं, इसलिए इसे अक्सर " (कॉल) स्टैक " कहा जाता है ।
क्योंकि अधिकांश सी ++ कार्यान्वयन (विशेष रूप से आईएसए-स्तरीय मूल कोड को लक्षित करने वाले और विधानसभा भाषा को इसके तत्काल आउटपुट के रूप में उपयोग करते हैं) इस तरह की रणनीतियों का उपयोग करते हैं, इस तरह के एक भ्रमित "आवंटन" योजना लोकप्रिय है। इस तरह के आबंटन (साथ ही सौदे) मशीन चक्र खर्च करते हैं, और यह तब महंगा हो सकता है जब (गैर-अनुकूलित) कॉल अक्सर होते हैं, भले ही आधुनिक सीपीयू माइक्रोआर्किटेक्चर में कॉमन कोड पैटर्न के लिए हार्डवेयर द्वारा कार्यान्वित जटिल अनुकूलन हो सकते हैं (जैसे कि इसका उपयोग करना) कार्यान्वयन / निर्देशों में स्टैक इंजन )।PUSH
POP
लेकिन वैसे भी, सामान्य तौर पर, यह सच है कि स्टैक फ्रेम आवंटन की लागत फ्री स्टोर को संचालित करने वाले आवंटन फ़ंक्शन के लिए कॉल की तुलना में काफी कम है (जब तक कि यह पूरी तरह से अनुकूलित न हो) , जिसमें स्वयं सैकड़ों हो सकते हैं (यदि लाखों नहीं हैं :-) स्टैक पॉइंटर और अन्य राज्यों को बनाए रखने के लिए ऑपरेशन। आवंटन कार्य आम तौर पर होस्ट किए गए वातावरण (जैसे ओएस द्वारा प्रदान की गई रनटाइम) द्वारा प्रदान किए गए एपीआई पर आधारित होते हैं। फ़ंक्शंस कॉल्स के लिए स्वचालित ऑब्जेक्ट रखने के उद्देश्य से भिन्न, ऐसे आवंटन सामान्य-purposed हैं, इसलिए उनके पास स्टैक की तरह फ्रेम संरचना नहीं होगी। परंपरागत रूप से, वे ढेर (या कई ढेर) नामक पूल के भंडारण से जगह आवंटित करते हैं । "स्टैक" से अलग, यहां अवधारणा "हीप" डेटा संरचना का उपयोग करने का संकेत नहीं देती है;यह दशकों पहले प्रारंभिक भाषा कार्यान्वयन से लिया गया है । (BTW, कॉल स्टैक आमतौर पर प्रोग्राम या थ्रेड स्टार्टअप में पर्यावरण द्वारा ढेर से निश्चित या उपयोगकर्ता द्वारा निर्दिष्ट आकार के साथ आवंटित किया जाता है।) उपयोग के मामलों की प्रकृति ढेर से अधिक जटिल (पुश या पॉप की तुलना में) आवंटन और डीललोक बनाती है। स्टैक फ्रेम), और हार्डवेयर द्वारा सीधे अनुकूलित किया जाना संभव नहीं है।
मेमोरी एक्सेस पर प्रभाव
सामान्य स्टैक आवंटन हमेशा नए फ्रेम को शीर्ष पर रखता है, इसलिए इसमें काफी अच्छा स्थान है। यह कैश के अनुकूल है। OTOH, फ्री स्टोर में बेतरतीब ढंग से आवंटित मेमोरी में ऐसी कोई संपत्ति नहीं है। आईएसओ सी ++ 17 के बाद से, पूल संसाधन टेम्पलेट प्रदान किए गए हैं <memory>
। इस तरह के इंटरफ़ेस का प्रत्यक्ष उद्देश्य लगातार आवंटन के परिणामों को स्मृति में एक साथ बंद करने की अनुमति देना है। यह इस तथ्य को स्वीकार करता है कि यह रणनीति आमतौर पर समकालीन कार्यान्वयन के साथ प्रदर्शन के लिए अच्छी है, उदाहरण के लिए आधुनिक आर्किटेक्चर में कैश के अनुकूल होना। यह आवंटन के बजाय पहुंच के प्रदर्शन के बारे में है , हालांकि।
संगामिति
मेमोरी के समवर्ती उपयोग की अपेक्षा स्टैक और ढेर के बीच अलग-अलग प्रभाव हो सकते हैं। एक कॉल स्टैक आमतौर पर C ++ कार्यान्वयन में निष्पादन के एक धागे के स्वामित्व में होता है। OTOH, ढेर अक्सर एक प्रक्रिया में धागे के बीच साझा किए जाते हैं । इस तरह के ढेर के लिए, आवंटन और सौदे के कार्यों को डेटा रेस से साझा आंतरिक प्रशासनिक डेटा संरचना की रक्षा करना है। नतीजतन, आंतरिक आवंटन के संचालन के कारण ढेर आवंटन और डीलॉक्लेशन अतिरिक्त ओवरहेड हो सकते हैं।
अंतरिक्ष क्षमता
उपयोग के मामलों की प्रकृति और आंतरिक डेटा संरचनाओं के कारण, ढेर आंतरिक मेमोरी विखंडन से पीड़ित हो सकते हैं , जबकि स्टैक नहीं करता है। मेमोरी आवंटन के प्रदर्शन पर इसका सीधा प्रभाव नहीं पड़ता है, लेकिन वर्चुअल मेमोरी वाले सिस्टम में , कम स्थान दक्षता मेमोरी एक्सेस के समग्र प्रदर्शन को कम कर सकती है। यह विशेष रूप से भयानक है जब HDD का उपयोग भौतिक मेमोरी के स्वैप के रूप में किया जाता है। यह काफी लंबी विलंबता पैदा कर सकता है - कभी-कभी अरबों चक्र।
ढेर आवंटन की सीमाएं
हालांकि ढेर आवंटन वास्तव में ढेर आवंटन की तुलना में प्रदर्शन में बेहतर होते हैं, लेकिन निश्चित रूप से इसका मतलब यह नहीं है कि ढेर आवंटन हमेशा ढेर आवंटन की जगह ले सकते हैं।
सबसे पहले, आईएसओ सी ++ के साथ पोर्टेबल तरीके से रनटाइम पर निर्दिष्ट आकार के साथ स्टैक पर जगह आवंटित करने का कोई तरीका नहीं है। alloca
जीएएल के वीएलए (चर-लंबाई सरणी) जैसे कार्यान्वयन द्वारा प्रदान किए गए एक्सटेंशन हैं , लेकिन उनसे बचने के लिए कारण हैं। (IIRC, लिनक्स स्रोत वीएलए के उपयोग को हाल ही में हटाता है।) (यह भी ध्यान दें कि आईएसओ C99 में VLA अनिवार्य है, लेकिन ISO C11 समर्थन को वैकल्पिक बनाता है।)
दूसरा, स्टैक स्पेस थकावट का पता लगाने के लिए कोई विश्वसनीय और पोर्टेबल तरीका नहीं है। इसे अक्सर स्टैक ओवरफ़्लो कहा जाता है (हम्म, इस साइट की व्युत्पत्ति) , लेकिन संभवतः अधिक सटीक, स्टैक ओवररन । वास्तव में, यह अक्सर अमान्य मेमोरी एक्सेस का कारण बनता है, और प्रोग्राम की स्थिति तब दूषित (... या शायद बदतर, एक सुरक्षा छेद) होती है। वास्तव में, आईएसओ सी ++ में "स्टैक" की कोई अवधारणा नहीं है और संसाधन समाप्त होने पर इसे अपरिभाषित व्यवहार करता है । स्वचालित वस्तुओं के लिए कितना कमरा छोड़ा जाना चाहिए, इसके बारे में सतर्क रहें।
यदि स्टैक स्पेस बाहर निकलता है, तो स्टैक में बहुत अधिक ऑब्जेक्ट आवंटित किए जाते हैं, जो फ़ंक्शंस के बहुत सक्रिय कॉल या स्वचालित ऑब्जेक्ट के अनुचित उपयोग के कारण हो सकता है। ऐसे मामले बग के अस्तित्व का सुझाव दे सकते हैं, उदाहरण के लिए, सही निकास की स्थिति के बिना एक पुनरावर्ती फ़ंक्शन कॉल।
फिर भी, गहरी पुनरावर्ती कॉल कभी-कभी वांछित होती हैं। अनबाउंड सक्रिय कॉल (जहां कॉल की गहराई केवल कुल मेमोरी द्वारा सीमित है) के समर्थन की आवश्यकता वाली भाषाओं के कार्यान्वयन में, यह (समकालीन) देशी कॉल स्टैक को सीधे लक्षित भाषा सक्रियण रिकॉर्ड के रूप में विशिष्ट C ++ कार्यान्वयन की तरह उपयोग करना असंभव है। समस्या के आसपास काम करने के लिए, सक्रियण रिकॉर्ड के निर्माण के वैकल्पिक तरीकों की आवश्यकता होती है। उदाहरण के लिए, एसएमएल / एनजे स्पष्ट रूप से ढेर पर फ्रेम आवंटित करता है और कैक्टस स्टैक का उपयोग करता है । इस तरह के सक्रियण रिकॉर्ड फ़्रेम का जटिल आवंटन आमतौर पर कॉल स्टैक फ़्रेम के रूप में तेज़ नहीं होता है। हालांकि, यदि उचित पूंछ पुनरावृत्ति की गारंटी के साथ ऐसी भाषाओं को आगे लागू किया जाता हैऑब्जेक्ट लैंग्वेज में सीधा स्टैक एलोकेशन (यानी, भाषा में "ऑब्जेक्ट" को संदर्भ के रूप में संग्रहीत नहीं किया जाता है, लेकिन मूल आदिम मान जो अनसेकेडेड सी ++ ऑब्जेक्ट्स के लिए एक मैप किया जा सकता है) और भी अधिक जटिल है सामान्य रूप से प्रदर्शन दंड। ऐसी भाषाओं को लागू करने के लिए C ++ का उपयोग करते समय, प्रदर्शन प्रभावों का अनुमान लगाना मुश्किल है।