provably संभव नहीं:
समय b
द्वारा कहा जाता है, स्टैक में अंतरिक्ष द्वारा प्रयुक्त है arg1
(IL ढेर है, तो संभवतः यह कभी नहीं भी एक ढेर में डाल दिया गया था, लेकिन पर enregistered किया गया था कॉल) अभी भी arg1
द्वारा उपयोग की जाने वाली गारंटी नहीं है।
विस्तार से, यदि arg1
एक संदर्भ-प्रकार है, तो जिस वस्तु को संदर्भित किया गया है उसे कचरा नहीं किया गया है, अगर b
पर कॉल के बाद इसका उपयोग नहीं किया जाता है।
संपादित:
थोड़ा और विस्तार से, अपनी टिप्पणी से पता चलता है के बाद से आप इस grokking नहीं कर रहे हैं और अभी भी लगता है कि यह संभव हो जाना चाहिए।
जिटर द्वारा उपयोग किए जाने वाले कॉलिंग सम्मेलनों को किसी भी प्रासंगिक मानकों के लिए चश्मा में निर्दिष्ट नहीं किया गया है, जो कार्यान्वयन करने वालों को सुधार करने की आजादी देता है। वे वास्तव में 32-बिट और 64-बिट संस्करणों और विभिन्न रिलीज़ के बीच भिन्न होते हैं।
हालांकि, एमएस लोगों के लेख बताते हैं कि इस्तेमाल किया जाने वाला सम्मेलन __fastcall सम्मेलन के समान है। आपके कॉल में a
, arg1
को ईडीएक्स रजिस्टर *, और arg2
में ईडीएक्स रजिस्टर में रखा जाएगा (मैं 32-बिट x86 मानकर सरलीकृत कर रहा हूं, amd64 के साथ और भी तर्क पंजीकृत हैं) कोर के कोड पर चल रहा है । arg3
ढेर पर धकेल दिया जाएगा और वास्तव में स्मृति में मौजूद होगा।
ध्यान दें कि इस बिंदु पर, कोई स्मृति स्थान नहीं है जिसमें arg1
और arg2
मौजूद हैं, वे केवल एक सीपीयू रजिस्टर में हैं।
विधि को निष्पादित करने के दौरान, रजिस्टरों और मेमोरी को आवश्यकतानुसार उपयोग किया जाता है। और b
कहा जाता है।
अब, a
जरूरत के लिए arg1
या arg2
धक्का कि इससे पहले कि यह b
कॉल करना होगा जा रहा है अगर। लेकिन अगर ऐसा नहीं होता है, तो यह नहीं होगा - और इस आवश्यकता को कम करने के लिए चीजों को फिर से आदेश दिया जा सकता है। इसके विपरीत, उन रजिस्टरों का उपयोग पहले से ही इस बिंदु से पहले से ही किया जा सकता है - जिटर बेवकूफ नहीं है, इसलिए यदि इसे स्टैक पर एक रजिस्टर या स्लॉट की आवश्यकता होती है और बाकी की विधि के लिए कोई भी अप्रयुक्त नहीं होता है, तो यह जा रहा है उस जगह का पुन: उपयोग करने के लिए। (उस मामले के लिए, इसके ऊपर के स्तर पर, सी # कंपाइलर आभासी ढेर में स्लॉट का पुन: उपयोग करेगा जो आईएल उत्पादित करता है)।
तो, जब b
कहा जाता है, arg4
रजिस्टर ECX, EDX में arg5
और arg6
ढेर पर धकेल दिया में रखा गया है। इस बिंदु पर, arg1
और arg2
मौजूद नहीं है और अब आप यह पता नहीं लगा सकते कि आप रीसाइक्लिंग के बाद पुस्तक को पढ़ सकते हैं और टॉयलेट पेपर में बदल सकते हैं।
(दिलचस्प नोट यह है कि एक ही स्थिति में एक ही तर्क के साथ दूसरे को कॉल करने की विधि के लिए यह बहुत आम है, जिस स्थिति में ईसीएक्स और ईडीएक्स को अकेला छोड़ दिया जा सकता है)।
फिर, b
रिटर्न, EAX रजिस्टर, या EDX में अपनी वापसी मान डाल: EAX जोड़ी या इसे करने के लिए EAX की ओर इशारा करते के साथ स्मृति में, आकार के आधार पर a
कुछ और काम है कि रजिस्टर में अपनी वापसी डालने से पहले करता है, और शीघ्र।
अब, यह माना जाता है कि कोई अनुकूलन नहीं किया गया है। यह संभव है कि वास्तव में, b
बिल्कुल भी नहीं बुलाया गया था, बल्कि इसके कोड को रेखांकित किया गया था। इस मामले में क्या रजिस्टर में या स्टैक पर मूल्य - और बाद के मामले में, जहां वे ढेर पर थे, अब b
के हस्ताक्षर के साथ कुछ भी नहीं करना है और a
के दौरान प्रासंगिक मान कहां हैं निष्पादन, और यह b
पर b
पर किसी अन्य "कॉल" के मामले में अलग होगा, a
से b
पर किसी भी "कॉल" के मामले में, a
की पूरी कॉल के बाद b
पर कॉल किया जा सकता था एक मामले में, किसी अन्य में रेखांकित नहीं है, और एक दूसरे में अलग-अलग रेखांकित किया गया है। उदाहरण के लिए, arg4
किसी अन्य कॉल द्वारा लौटाए गए मूल्य से सीधे आया, यह इस बिंदु पर ईएक्स रजिस्टर में हो सकता है, जबकि arg5
ईसीएक्स में था क्योंकि यह arg1
और arg6
जैसा था, के बीच में कहीं भी आधा रास्ता था a
द्वारा स्टैक-स्पेस का उपयोग किया जा रहा है।
एक और संभावना है कि b
करने के लिए कॉल एक पूंछ-कॉल कि हटा दिया गया था: क्योंकि b
करने के लिए कॉल अपनी वापसी मान तुरंत a
से भी लौट आए (या कुछ अन्य संभावनाओं) के लिए जा रहा था, तो बजाय धक्का ढेर, a
द्वारा उपयोग किए जाने वाले मानों को जगह में बदल दिया गया है, और वापसी पता बदल दिया गया है ताकि b
से वापसी a
नामक विधि पर वापस आ जाए, कुछ काम छोड़कर (और कुछ हद तक स्मृति उपयोग को कम करना कार्यात्मक शैली दृष्टिकोण जो ढेर को बहते हैं बल्कि काम करते हैं और वास्तव में अच्छी तरह से काम करते हैं)। इस मामले में, b
पर कॉल के दौरान, a
पर पैरामीटर पूरी तरह से चले गए हैं, यहां तक कि जो स्टैक पर थे।
यह बेहद बहस योग्य है कि क्या इस अंतिम मामले को भी अनुकूलन माना जाना चाहिए; कुछ भाषाएं इस पर निर्भर करती हैं कि इसके साथ किया जा रहा है, वे अच्छे प्रदर्शन देते हैं और यदि वे भी काम करते हैं तो वे भयानक प्रदर्शन देते हैं (स्टैक को बहने के बजाए)।
अन्य सभी अनुकूलन हो सकते हैं। अन्य सभी अनुकूलन होना चाहिए - यदि .NET टीम या मोनो टीम ऐसा कुछ करती है जो मेरे कोड को तेज़ी से बनाती है या कम मेमोरी का उपयोग करती है लेकिन अन्यथा कुछ भी करने के बिना, वैसे ही व्यवहार करती है, तो मैं एक नहीं होगा उपालंभ देना!
और यह मानते हुए कि पहली बार सी # लिखने वाले व्यक्ति ने पैरामीटर के मान को कभी नहीं बदला, जो निश्चित रूप से सत्य नहीं होने वाला है।इस कोड पर विचार करें:
IEnumerable<T> RepeatedlyInvoke(Func<T> factory, int count)
{
if(count < 0)
throw new ArgumentOutOfRangeException();
while(count-- != 0)
yield return factory();
}
यहां तक कि अगर सी # संकलक और घबराना इस तरह के एक बेकार तरीका है कि आप मानकों की गारंटी दे सकें ऊपर वर्णित तरीकों से बदले नहीं गए हैं में तैयार किया गया था, तुम कैसे जानते हो सकता है क्या count
पहले से ही था factory
के आविष्कार के भीतर से किया गया है? पहले कॉल पर भी यह अलग है, और यह उपरोक्त की तरह नहीं है अजीब कोड है।
तो, सारांश में:
- जिटर: पैरामीटर अक्सर enregistered कर रहे हैं। आप रजिस्ट्रारों में 4 पॉइंटर, संदर्भ या पूर्णांक पैरामीटर और 4 फ़्लोटिंग-पॉइंट पैरामीटर डालने के लिए रजिस्टरों और amd64 में 2 पॉइंटर, संदर्भ या पूर्णांक पैरामीटर डालने की उम्मीद कर सकते हैं। उनके पास उन्हें पढ़ने के लिए कोई स्थान नहीं है।
- जिटर: ढेर पर पैरामीटर अक्सर ओवर-लिखित होते हैं।
- जिटर: बिल्कुल वास्तविक कॉल नहीं हो सकता है, इसलिए पैरामीटर देखने के लिए कोई जगह नहीं है क्योंकि वे कहीं भी हो सकते हैं।
- जिटर: "कॉल" अंतिम फ्रेम के समान फ्रेम का पुनः उपयोग कर सकता है।
- कंपाइलर: आईएल स्थानीय लोगों के लिए स्लॉट का पुनः उपयोग कर सकता है।
- मानव: प्रोग्रामर पैरामीटर मान बदल सकता है।
है, उन सभी से, पृथ्वी पर कैसे यह पता है कि arg1
था संभव होने जा रहा है?
अब, कचरा संग्रह के अस्तित्व में जोड़ें। कल्पना कीजिए कि अगर हम जादुई रूप से जान सकें कि arg1
इस सब के बावजूद क्या था। यदि यह ढेर पर किसी ऑब्जेक्ट का संदर्भ था, तो यह अभी भी हमें अच्छा नहीं कर सकता है, क्योंकि अगर उपर्युक्त सभी का मतलब है कि स्टैक पर कोई और संदर्भ सक्रिय नहीं था - और यह स्पष्ट होना चाहिए कि यह निश्चित रूप से होता है - और जीसी में लाता है, तो ऑब्जेक्ट एकत्र किया जा सकता था। तो हम सभी जादुई रूप से पकड़ ले सकते हैं जो किसी चीज का संदर्भ नहीं है जो वास्तव में मौजूद नहीं है - वास्तव में ढेर में किसी क्षेत्र के लिए संभवतः किसी अन्य चीज़ के लिए उपयोग किया जा रहा है, बैंग पूरे ढांचे की पूरी तरह की सुरक्षा चला जाता है!
यह प्रतिबिंब आईएल प्राप्त करने के लिए तुलनीय थोड़ी सी भी बिट में नहीं है क्योंकि:
- आईएल स्थिर है, बल्कि समय में एक भी बिंदु पर सिर्फ एक राज्य की तुलना में। इसी तरह, हम पुस्तकालय से हमारी पसंदीदा पुस्तकों की एक प्रति प्राप्त कर सकते हैं, इससे पहले कि हम उन्हें पहली बार पढ़ सकें, हम उनकी प्रतिक्रिया वापस ले सकते हैं।
- आईएल किसी भी तरह से इनलाइनिंग इत्यादि के प्रभाव को प्रतिबिंबित नहीं करता है। यदि प्रत्येक बार वास्तव में उपयोग किए जाने पर कॉल को रेखांकित किया गया था, और फिर हमने उस विधि के
MethodBody
प्राप्त करने के लिए प्रतिबिंब का उपयोग किया, तो तथ्य यह है कि इसका सामान्य रूप से रेखांकित अप्रासंगिक है।
रूपरेखा, AOP, और अवरोधन के बारे में अन्य उत्तर में सुझाव के रूप पास के रूप में आप प्राप्त करने के लिए जा रहे हैं कर रहे हैं।
* वास्तव में, this
उदाहरण सदस्यों के लिए वास्तविक पहला पैरामीटर है। आइए दिखाएं कि सबकुछ स्थिर है इसलिए हमें इसे इंगित करने की आवश्यकता नहीं है।
दिलचस्प सवाल ... सी # में संभव नहीं, लेकिन शायद आईएल में या प्रतिबिंब का उपयोग कर? –
मेरी शर्त यह है कि यह असंभव है। यह एक अच्छा अपरंपरागत, _open_ साक्षात्कार सवाल करेगा हालांकि (कल्पना करें कि यह संभव है - आपको कैसा लगता है कि यह काम करेगा?), सबसे आम "oddball प्रश्न" से बेहतर तरीका; सी # और सीएलआर इत्यादि के किसी के समग्र ज्ञान को प्रकट करने के लिए एक अच्छा प्रारंभिक बिंदु –
सैद्धांतिक रूप से, यह संभव होना चाहिए। साधारण स्टैकट्रैस उन कार्यों के बारे में जानकारी देता है जिन्हें बुलाया जा रहा है, साथ ही साथ उनके तर्क प्रकार, और कार्यों को कॉल करने के लिए उपयोग किए जाने वाले सभी चर को कहीं भी ढेर में संग्रहीत किया जाना चाहिए (हालांकि, संभवतः स्थानीय चर के रूप में)। –