2012-09-16 16 views
8

में स्थानीय चर क्यों हैं, एक स्टैक-आधारित मध्यवर्ती भाषा, जैसे सीआईएल या जावा बाइटकोड में, स्थानीय चर क्यों हैं? कोई केवल स्टैक का उपयोग कर सकता है। हाथ से तैयार आईएल के लिए इतना आसान नहीं हो सकता है, लेकिन एक कंपाइलर निश्चित रूप से ऐसा कर सकता है। लेकिन मेरा सी # कंपाइलर नहीं करता है।स्टैक-आधारित आईएल बाइटकोड

दोनों स्टैक और स्थानीय चर विधि के लिए निजी हैं और विधि लौटने पर दायरे से बाहर निकलते हैं। तो विधि के बाहर से (अन्य धागे से) दिखाई देने वाले दुष्प्रभावों के साथ इसका कोई संबंध नहीं हो सका।

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

दूसरी तरफ, सी # कंपाइलर स्थानीय चर के लिए भार और स्टोर उत्पन्न करता है, भले ही ऑप्टिमाइज़ेशन सक्षम हो। क्यूं कर?


उदाहरण के लिए लें, तो निम्न काल्पनिक उदाहरण कोड:

static int X() 
{ 
    int a = 3; 
    int b = 5; 
    int c = a + b; 
    int d; 
    if (c > 5) 
     d = 13; 
    else 
     d = 14; 
    c += d; 
    return c; 
} 

जब सी # में संकलित, अनुकूलन के साथ, यह पैदा करता है:

ldc.i4.3  # Load constant int 3 
    stloc.0   # Store in local var 0 
    ldc.i4.5  # Load constant int 5 
    stloc.1   # Store in local var 1 
    ldloc.0   # Load from local var 0 
    ldloc.1   # Load from local var 1 
    add    # Add 
    stloc.2   # Store in local var 2 
    ldloc.2   # Load from local var 2 
    ldc.i4.5  # Load constant int 5 
    ble.s label1 # If less than, goto label1 
    ldc.i4.s 13  # Load constant int 13 
    stloc.3   # Store in local var 3 
    br.s label2  # Goto label2 
label1: 
    ldc.i4.s 14  # Load constant int 14 
    stloc.3   # Store in local var 3 
label2: 
    ldloc.2   # Load from local var 2 
    ldloc.3   # Load from local var 3 
    add    # Add 
    stloc.2   # Store in local var 2 
    ldloc.2   # Load from local var 2 
    ret    # Return the value 

नोट से चार भार और दुकानों स्थानीय चर मैं किसी भी स्थानीय चर का उपयोग किये बिना सटीक एक ही ऑपरेशन (स्पष्ट निरंतर प्रचार अनुकूलन को अनदेखा कर सकता हूं) लिख सकता हूं।

ldc.i4.3  # Load constant int 3 
    ldc.i4.5  # Load constant int 5 
    add    # Add 
    dup    # Duplicate top stack element 
    ldc.i4.5  # Load constant int 5 
    ble.s label1 # If less than, goto label1 
    ldc.i4.s 13  # Load constant int 13 
    br.s label2  # Goto label2 
label1: 
    ldc.i4.s 14  # Load constant int 14 
label2: 
    add    # Add 
    ret    # Return the value 

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

+3

आप अपने उदाहरण में प्रदर्शन के रूप में * हमेशा * इतना आसान परिवर्तन नहीं कर सकते हैं। –

+0

क्या यह सवाल पूछ रहा है कि क्यों "नामित स्लॉट" आवश्यक हैं * या क्यों सी # "अनुकूलित" आउटपुट अत्यधिक वर्बोज़ दिखता है (उदाहरण के लिए * इस मामले में * उपयोग किया जाता है)? –

+0

यहां तक ​​कि यदि कुछ मामलों में "नामित स्लॉट" की आवश्यकता होती है या उपयोगी होती है (कौन सा मामला?), ऑप्टिमाइज़िंग कंपाइलर अधिकांश लोड और स्टोर को क्यों खत्म नहीं करता है? यह बहुत तुच्छ लगता है। मेरा कुछ छूट रहा है। – Virtlink

उत्तर

4

स्थिति के आधार पर, लेकिन विशेष रूप से जब कॉल शामिल होते हैं जहां पैरामीटर को कॉल से मेल खाने के लिए फिर से आदेश दिया जाना होता है, तो आपके पास आपके पास रजिस्ट्रार या चर नहीं होने पर शुद्ध स्टैक पर्याप्त नहीं होता है। यदि आप केवल इस स्टैक को बनाना चाहते हैं, तो आपको अतिरिक्त स्टैक मैनिपुलेशन अबालिटी की आवश्यकता होगी, जैसे ढेर के दो शीर्ष आइटमों का आदान-प्रदान/स्वैप करने की क्षमता।

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

यह भी याद रखें कि .NET में आप संदर्भ के अनुसार पैरामीटर पास कर सकते हैं, आप स्थानीय चर के बिना इस विधि कॉल के लिए आईएल कैसे बना सकते हैं?

bool TryGet(int key, out string value) {} 
+0

मुझे नहीं पता कि यह हस्ताक्षर कैसे कुछ भी सवाल उठाता है। आईएल के लिए "writeToOutParam" ऑपरेशन (एक दुष्प्रभाव) हो सकता है जो पूरी तरह से ढेर-आधारित था। –

+1

मुझे लगता है कि लुसेरो कॉल के कॉलर के पक्ष का जिक्र कर रहा है। जब कोई स्थानीय चर नहीं होता है, तो स्थानीय चर का पता 'TryGet' विधि को दूसरी तर्क के रूप में पास किया जाता है? लेकिन यह निश्चित रूप से, एक स्टैक स्लॉट के लिए 'सूचक' हो सकता है। और रजिस्टरों में कैश होने के लिए बेहतर उम्मीदवारों के ढेर पर मूल्य नहीं हैं? – Virtlink

0

यह उत्तर पूरी तरह से सट्टा है - लेकिन मुझे संदेह है कि उत्तर में 3 भाग हैं।

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

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

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

एक साथ रखो, 1 का मतलब है कि यह कठिन है और एक गैर-मामूली लागत है, 2 का मतलब है कि असली दुनिया के मामले जहां यह मूल्यवान होगा, और 3 का मतलब है कि 1 और 2 अप्रासंगिक हैं।

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

+0

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

+0

लेकिन मैं अभी भी पूरी तरह से अनावश्यक स्थानीय चर (जैसे 'ldfld x; stloc.1; ldarg.0; ldloc.1; ldc.i4.1; जोड़ें; stfld x;' - क्या उद्देश्य करता है 'loc.1' सेवा करते हैं?) जब वे कुछ भी नहीं जोड़ते हैं। या 'ldarg.0 का उपयोग कर; ldarg.0; 'जब' ldarg.0; डुप्ल; 'सभी स्थितियों में सुरक्षित लगेगा? – NetMage

संबंधित मुद्दे