यदि प्रोग्राम नेस्टेड फ़ंक्शन कॉल को इनलाइन किया जा सकता है तो कॉल स्टैक का उपयोग क्यों करते हैं?


33

कंपाइलर के पास ऐसा प्रोग्राम क्यों नहीं है:

function a(b) { return b^2 };
function c(b) { return a(b) + 5 };

और इसे इस तरह एक कार्यक्रम में परिवर्तित करें:

function c(b) { return b^2 + 5 };

जिससे कंप्यूटर को c (b) के रिटर्न एड्रेस को याद रखने की आवश्यकता समाप्त हो जाती है?

मुझे लगता है कि प्रोग्राम को स्टोर करने और इसके संकलन (क्रमशः) का समर्थन करने के लिए आवश्यक हार्ड डिस्क स्थान और रैम की आवश्यकता है यही कारण है कि हम कॉल स्टैक का उपयोग करते हैं। क्या वो सही है?


30
देखें कि यदि आप किसी सार्थक आकार के साथ किसी प्रोग्राम पर ऐसा करते हैं तो क्या होगा। विशेष रूप से, फ़ंक्शन एक से अधिक स्थानों से बुलाए जाते हैं।
user253751

10
इसके अलावा, कभी-कभी कंपाइलर को पता नहीं होता है कि कौन सा फंक्शन कहा जाता है! मूर्खतापूर्ण उदाहरण:window[prompt("Enter function name","")]()
user253751

26
function(a)b { if(b>0) return a(b-1); }स्टैक के बिना आप कैसे लागू करते हैं ?
pjc50

8
कार्यात्मक प्रोग्रामिंग का संबंध कहां है?
मस्तोव

14
@ pjc50: यह पूंछ पुनरावर्ती है, इसलिए संकलक इसे एक परिवर्तनशील के साथ एक लूप में अनुवाद करता है b। लेकिन बिंदु लिया गया है, सभी पुनरावर्ती कार्य पुनरावृत्ति को समाप्त नहीं कर सकते हैं, और यहां तक ​​कि जब फ़ंक्शन सिद्धांत रूप में हो सकता है, तो कंपाइलर ऐसा करने के लिए पर्याप्त स्मार्ट नहीं हो सकता है।
स्टीव जेसप

जवाबों:


75

इसे "इनलाइनिंग" कहा जाता है और कई कंपाइलर उन मामलों में अनुकूलन रणनीति के रूप में करते हैं जहां यह समझ में आता है।

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

और स्पष्ट रूप से यह केवल "निजी" कार्यों के लिए संभव है। बाहरी कॉलर्स के लिए जो कार्य उजागर होते हैं, उन्हें दूर नहीं किया जा सकता है, कम से कम गतिशील लिंकिंग वाली भाषाओं में नहीं।


7
@ ब्लरफ्ल: आधुनिक संकलक को वास्तव में हेडर में परिभाषा की आवश्यकता नहीं है; वे अनुवाद इकाइयों में इनलाइन कर सकते हैं। हालांकि इसके लिए एक सभ्य लिंकर की आवश्यकता होती है। शीर्ष लेख फ़ाइलों में परिभाषाएँ डंबर्स लिंकर्स के लिए एक समाधान हैं।
एमएसलटर्स

3
"कार्य जो बाहरी कॉलर्स के लिए उजागर होते हैं, उन्हें अनुकूलित नहीं किया जा सकता है" - फ़ंक्शन मौजूद है, लेकिन किसी भी दिए गए कॉल साइट को (या तो आपके स्वयं के कोड में, या यदि उनके पास स्रोत है, तो बाहरी कॉलर्स) इनबिल्ट किया जा सकता है।
रैंडम 832

14
वाह, एक उत्तर के लिए 28 अपवोट्स जो भी कारण का उल्लेख नहीं करता है कि सब कुछ असंभव क्यों है: पुनरावृत्ति।
मस्तोव

3
@ आर ..: एलटीओ लिंक टाइम ऑप्टिमाइज़ेशन है, एलओएडी टाइम ऑप्टिमाइज़ेशन नहीं।
MSalters 1

2
@immibis: लेकिन अगर वह स्पष्ट ढेर संकलक द्वारा शुरू की गई है, तो ढेर कि है कॉल स्टैक।
user2357112

51

आपके प्रश्न के दो भाग हैं: सभी में कई फ़ंक्शन क्यों हैं (उनकी परिभाषा के साथ फ़ंक्शन कॉल को बदलने के बजाय) और उन कार्यों को कॉल स्टैक के साथ लागू करने के बजाय सांख्यिकीय रूप से उनके डेटा को कहीं और क्यों आवंटित करें?

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

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

इसके अतिरिक्त, जब आप कर सकते हैं तब भी इनलाइन कार्यों को नहीं करने के कई कारण हैं:

  1. यह तेजी से जरूरी नहीं है। स्टैक फ्रेम को स्थापित करना और इसे फाड़ना संभवतः कई बड़े या लूपिंग कार्यों के लिए एक दर्जन एकल-चक्र निर्देश हैं, जो कि उनके निष्पादन समय का 0.1% भी नहीं है।
  2. यह धीमा हो सकता है। कोड के डुप्लीकेशन में लागत होती है, उदाहरण के लिए, यह निर्देश कैश में अधिक दबाव डालेगा।
  3. कुछ कार्य बहुत बड़े होते हैं और कई स्थानों से बुलाए जाते हैं, उन्हें हर जगह पर लाना द्विआधारी तक बढ़ जाता है जो उचित है।
  4. कंपाइलरों में अक्सर बहुत बड़े कार्यों के साथ एक कठिन समय होता है। बाकी सब कुछ बराबर हो रहा है, आकार 2 * N का एक फ़ंक्शन 2 * T से अधिक समय लेता है जहां आकार N का फ़ंक्शन T समय लेता है।

1
मैं बिंदु 4 से हैरान हूं। इसका कारण क्या है?
जैक्सबी

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

2
"आपके पास वास्तव में दो प्रश्न हैं" नहीं, मैं नहीं। सिर्फ एक - एक फ़ंक्शन कॉल को केवल एक प्लेसहोल्डर के रूप में क्यों नहीं माना जाता है जो कंपाइलर कह सकता है, कहे गए फ़ंक्शन के कोड से बदल सकता है?
चंद्रमन २३

4
@ Chandman239 फिर आपके शब्दों ने मुझे फेंक दिया। फिर भी, आपका प्रश्न विघटित हो सकता है जैसा कि मैं अपने उत्तर में करता हूं और मुझे लगता है कि यह एक उपयोगी परिप्रेक्ष्य है।

16

ढेर हमें नियमित रूप से रजिस्टरों की परिमित संख्या द्वारा लगाए गए सीमा को बायपास करने की अनुमति देते हैं।

कल्पना कीजिए कि 26 ग्लोबल्स "रजिस्टर अज़" (या यहां तक ​​कि 8080 चिप के केवल 7 बाइट-आकार के रजिस्टर हैं) और इस ऐप में आपके द्वारा लिखे गए प्रत्येक फ़ंक्शन इस फ्लैट सूची को साझा करते हैं।

एक भोली शुरुआत पहले फ़ंक्शन के लिए पहले कुछ रजिस्टर आवंटित करने के लिए होगी, और यह जानते हुए कि यह केवल 3 लिया, दूसरे फ़ंक्शन के लिए "डी" से शुरू करें ... आप जल्दी से बाहर भागते हैं।

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

आपका मूल कॉल फ्रेम बस यही है, और मानकीकृत मशीन कोड अनुक्रमों द्वारा संकलित और गिराए जाते हैं, जो संकलक के आसपास एक फ़ंक्शन से दूसरे फ़ंक्शन में डालता है। (यह एक लंबा समय हो गया है क्योंकि मुझे अपने सी स्टैक फ्रेम को याद करना था, लेकिन आप विभिन्न तरीकों से पढ़ सकते हैं कि कौन कौन से कर्तव्य X86_calling_conventions पर गिरता है ।)

(पुनरावृत्ति भयानक है, लेकिन यदि आपको कभी भी ढेर के बिना रजिस्टरों को टटोलना पड़ता है, तो आप वास्तव में ढेरियों की सराहना करेंगे।)


मुझे लगता है कि प्रोग्राम को स्टोर करने और इसके संकलन (क्रमशः) का समर्थन करने के लिए आवश्यक हार्ड डिस्क स्थान और रैम की आवश्यकता है यही कारण है कि हम कॉल स्टैक का उपयोग करते हैं। क्या वो सही है?

जबकि हम इन दिनों अधिक इनलाइन कर सकते हैं, ("अधिक गति" हमेशा अच्छा होता है; "कम kb of असेंबली" का अर्थ वीडियो स्ट्रीम की दुनिया में बहुत कम होता है) मुख्य सीमा कंपाइलर की कुछ विशेष प्रकार के कोड पैटर्न को समतल करने की क्षमता में है।

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

एक आधुनिक toolchain खुशी से इनलाइन एक polymorphically से परिभाषित कर सकते हैं समारोह जब यह फोन करने वाले (रों) के लिए पर्याप्त पता करने के लिए चपटा है वास्तव में obj का जो स्वाद है:

class Base {
    public: void act() = 0;
};
class Child1: public Base {
    public: void act() {};
};
void ActOn(Base* something) {
    something->act();
}
void InlineMe() {
    Child1 thingamabob;
    ActOn(&thingamabob);
}

ऊपर, संकलक जो भी अंदर के कार्य () के माध्यम से, इनलाइनम से, न ही सांख्यिकीय रूप से इनलाइनिंग पर सही रखने का चयन कर सकता है, न ही रनटाइम पर किसी भी vtables को छूने की आवश्यकता है।

लेकिन वस्तु का क्या स्वाद में किसी भी अनिश्चितता एक असतत फ़ंक्शन की कॉल के रूप में छोड़ देगा, भले ही एक ही समारोह के कुछ अन्य आमंत्रण रहे inlined।


11

मामले जो दृष्टिकोण को संभाल नहीं सकते हैं:

function fib(a) { if(a>2) return fib(a-1)+fib(a-2); else return 1; }

function many(a) { for(i = 1 to a) { b(i); };}

वहाँ रहे हैं भाषाओं और सीमित या कोई कॉल के ढेर के साथ प्लेटफार्मों। PIC माइक्रोप्रोसेसरों में 2 और 32 प्रविष्टियों के बीच एक हार्डवेयर स्टैक होता है । इससे डिज़ाइन की कमी होती है।

कोबोल प्रतिबंध प्रत्यावर्तन: https://stackoverflow.com/questions/27806812/in-cobol-is-it-possible-to-recursively-call-a-paragraph

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


12
आपका पहला उदाहरण बुनियादी पुनरावृत्ति है, और आप वहीं सही हैं। लेकिन आपका दूसरा उदाहरण एक अन्य फ़ंक्शन को कॉल करने वाले लूप के लिए प्रतीत होता है। इन-लाइनिंग फ़ंक्शन लूप को अनरोल करने से अलग है; फ़ंक्शन लूप को अनरोल किए बिना इन-लाइन किया जा सकता है। या मैंने कुछ सूक्ष्म विवरण याद किया है?
jpmc26

1
यदि आपका पहला उदाहरण फिबोनाची श्रृंखला को परिभाषित करने के लिए है, तो यह गलत है। (यह एक fibकॉल याद आ रही है ।)
पाओलो एबरमन

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

8

आप फ़ंक्शन को इनलाइन करना चाहते हैं , और अधिकांश ( अनुकूलन ) कंपाइलर ऐसा कर रहे हैं।

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

आसानी से संभव होने पर भी इनलाइन को नोटिस करें, हमेशा प्रभावी नहीं होता है: आप (वास्तव में आपका कंपाइलर) कोड आकार को इतना बढ़ा सकता है कि सीपीयू कैश (या ब्रांच प्रेडिक्टर ) कम कुशलता से काम करेगा, और इससे आपका प्रोग्राम रन होगा और धीमा।

मैं कार्यात्मक प्रोग्रामिंग शैली पर थोड़ा ध्यान केंद्रित कर रहा हूं , क्योंकि आपने अपने qestion को इस तरह टैग किया है।

ध्यान दें कि आपको किसी भी कॉल स्टैक की आवश्यकता नहीं है (कम से कम "कॉल स्टैक" अभिव्यक्ति के मशीन अर्थ में)। आप केवल ढेर का उपयोग कर सकते हैं।

तो, निरंतरता पर एक नज़र डालें और निरंतर उत्तीर्ण शैली (सीपीएस) और सीपीएस परिवर्तन के बारे में अधिक पढ़ें (सहजता से, आप हीप में आवंटित "कॉल फ़्रेम" के रूप में निरंतरता क्लोजर का उपयोग कर सकते हैं , और वे कॉल स्टैक की नकल करने के लिए सॉर्ट करते हैं; तो आप एक कुशल कचरा कलेक्टर की जरूरत है )।

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

मैं क्विनेक के लिस्प इन स्माल पीस बुक को भी पढ़ने की सलाह देता हूं , जिसमें निरंतरता और संकलन से संबंधित कई अध्याय हैं।

यह भी ध्यान रखें कि कुछ भाषाओं (जैसे ब्रेनफक ) या अमूर्त मशीनों (जैसे OISC , RAM ) में कॉलिंग की कोई सुविधा नहीं है, लेकिन फिर भी ट्यूरिंग-पूर्ण हैं , इसलिए आपको (सिद्धांत में) किसी भी फ़ंक्शन कॉल तंत्र की आवश्यकता नहीं है, भले ही यह बेहद सुविधाजनक है। BTW, कुछ पुराने निर्देश सेट आर्किटेक्चर (जैसे IBM / 370 ) में हार्डवेयर कॉल स्टैक या पुश कॉल मशीन निर्देश नहीं है (IBM / 370 में केवल एक शाखा और लिंक मशीन निर्देश था)

अंत में, यदि आपके पूरे कार्यक्रम (सभी आवश्यक पुस्तकालयों सहित) में कोई पुनरावृत्ति नहीं है, तो आप स्थैतिक स्थानों में प्रत्येक फ़ंक्शन के रिटर्न एड्रेस (और "स्थानीय" चर, जो वास्तव में स्थिर हो रहे हैं) को स्टोर कर सकते हैं। पुराने Fortran77 संकलक ने 1980 के दशक के प्रारंभ में (इसलिए संकलित कार्यक्रमों ने उस समय किसी कॉल स्टैक का उपयोग नहीं किया था)।


2
यह बहुत ही बहस का विषय है CPS में कोई "कॉल स्टैक" नहीं है। यह स्टैक पर नहीं है , साधारण रैम का रहस्यमय क्षेत्र जिसमें %espआदि के माध्यम से हार्डवेयर का थोड़ा सा समर्थन है , लेकिन यह अभी भी रैम के दूसरे क्षेत्र में एक उपयुक्त नाम स्पेगेटी स्टैक पर बराबर बहीखाता रखता है। रिटर्न एड्रेस, विशेष रूप से, निरंतरता में अनिवार्य रूप से एनकोडेड है। और निश्चित रूप से निरंतरता तेज नहीं है (और मुझे ऐसा लगता है कि यह ओपी में क्या था) इनलाइनिंग के माध्यम से कोई कॉल नहीं करता है

एपल के पुराने कागजात (और बेंचमार्किंग के साथ प्रदर्शित) का दावा है कि सीपीएस एक कॉल स्टैक होने के रूप में तेज़ हो सकता है।
बेसिल स्टारीनेविच

मुझे उस पर संदेह है लेकिन इस बात की परवाह किए बिना कि मैंने क्या दावा किया है।

1
दरअसल, यह 1980 के दशक के अंत में MIPS कार्य केंद्र था। शायद, वर्तमान पीसी पर कैश पदानुक्रम प्रदर्शन को थोड़ा अलग करेगा। एपल के दावों का विश्लेषण करने वाले कई कागजात (और वास्तव में, मौजूदा मशीनों पर, स्टैक आवंटन थोड़ा अधिक तेजी से हो सकता है-कुछ प्रतिशत-ध्यान से तैयार किए गए कचरा संग्रह की तुलना में)
बेसिल स्टैरनेविच

1
@ गिल्स: कई नए एआरएम कोर जैसे कॉर्टेक्स एम 0 और एम 3 (और शायद अन्य जैसे एम 4) के पास इंटरप्ट हैंडलिंग जैसी चीजों के लिए हार्डवेयर स्टैक सपोर्ट है। इसके अलावा, थम्ब इंस्ट्रक्शन सेट में STRM / STRM निर्देशों का एक सीमित उपसमूह शामिल होता है, जिसमें LR0-R7 के साथ / बिना LR के किसी भी संयोजन के साथ STRMDB R13 और PC के बिना R0-R7 / L7MIA के किसी भी संयोजन का LDRMIA R13 शामिल है, जो प्रभावी रूप से व्यवहार करता है। R13 एक स्टैक पॉइंटर के रूप में।
20

8

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

हालांकि, बड़े कार्य जो बदले में अन्य कार्यों को बुलाते हैं, अगर सब कुछ इनबिल्ड होता तो प्रोग्राम साइज में बहुत बड़ा विस्फोट हो सकता था।

कॉल करने योग्य कार्यों का पूरा बिंदु कुशल पुन: उपयोग को सुविधाजनक बनाना है, न केवल प्रोग्रामर द्वारा, बल्कि मशीन द्वारा ही, और जिसमें उचित मेमोरी या डिस्क-डिस्क पदचिह्न जैसे गुण शामिल हैं।

इसके लायक क्या है: आप कॉल स्टैक के बिना कॉल करने योग्य कार्य कर सकते हैं। उदाहरण के लिए: आईबीएम सिस्टम / 360। जब उस हार्डवेयर पर FORTRAN जैसी भाषाओं में प्रोग्रामिंग की जाती है, तो प्रोग्राम काउंटर (रिटर्न एड्रेस) को फंक्शन एंट्री पॉइंट से आगे आरक्षित मेमोरी के एक छोटे से सेक्शन में सहेजा जाएगा। यह पुन: प्रयोज्य कार्यों के लिए अनुमति देता है, लेकिन पुनरावृत्ति या बहु-थ्रेडेड कोड के लिए अनुमति नहीं देता है (पुनरावर्ती या पुन: प्रवेश पर कॉल का प्रयास पहले से सहेजे गए रिटर्न पते को अधिलेखित किया जाएगा)।

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.