2010-12-11 20 views
7
  1. जब किसी फ़ंक्शन (सी/सी ++) के अंदर एक वैश्विक चर का उपयोग किया जाता है, तो क्या इसे सीधे रजिस्टरों या स्टैक से लिया जाएगा?सी ++/सी-स्पेस स्तरीय प्रश्न

  2. क्यों बाध्य लूप (लूप के लिए) को महान लूप (जबकि लूप/करते समय) के अनुकूलन के लिए अधिक गुंजाइश माना जाता है?

  3. एक मूल्य वापस क्यों करना संदर्भ के आधार पर गुजरने के समान नहीं है?

यदि संभव हो तो कृपया विधानसभा स्तर के विवरण दें।

+9

(1) न तो, (2) वे नहीं हैं, (3) एनआरवीओ की वजह से यह नहीं होना चाहिए। –

+0

आप बिंदु 3 में क्या मतलब है? संदर्भ द्वारा कार्य करने के लिए तर्कों को पास करना, या फ़ंक्शन तर्कों में से किसी एक द्वारा फ़ंक्शन मान वापस करना। –

+0

@ पावेल: आरवीओ का अर्थ रिटर्न वैल्यू ऑप्टिमाइज़ेशन है, जिसका मतलब है कि एक प्रतिलिपि की लागत के बजाय मूल्य (जहां इसे वापस किया जाता है) का निर्माण करना है। जब यह अनुकूलन बढ़ता है, तो मूल्य से लौटने पर संदर्भ या सूचक को पारित करने से अधिक लागत नहीं होती है। –

उत्तर

1

1) वैश्विक चर स्थिर रूप से लिंकर द्वारा आवंटित किया गया है (यह मॉड्यूल के आधार से ऑफ़सेट हो सकता है, आवश्यक रूप से एक निश्चित पता नहीं)। फिर भी, एक फ़ंक्शन आमतौर पर से एक प्रत्यक्ष पता, और ऑफसेट + स्टैक पॉइंटर और ऑफसेट + ऑब्जेक्ट बेस पॉइंटर से एक क्लास फ़ील्ड से एक स्थानीय var पढ़ता है। वैश्विक चर के मान को बाद में पढ़ने के लिए में कैश किया जा सकता है, जब तक कि इसे "अस्थिर" घोषित न किया जाए।

2) इसकी नहीं वास्तव में एक के लिए/करते/विकल्प है, लेकिन कितना आसान है अपने पुनरावृत्तियों की संख्या की गणना करने के जबकि, ताकि संकलक उतारना है कि क्या करने के लिए और/या vectorize और तय करने में सक्षम होगा बात/या लूप समानांतर करें। उदाहरण के लिए, यहाँ संकलक पुनरावृत्तियों की संख्या पता होगा:

for(i=0; i<8; i++) { j = 1 << i; XXX } 

और यहाँ ऐसा नहीं होगा:

for(j=1; j<256; j<<=1) { XXX } 

छोरों के लिए हो सकता है सिर्फ अधिक बार एक संरचना जो आसान है कंपाइलर के लिए समझने के लिए।

3) यदि अपनी बुनियादी प्रकार (चार/लघु/पूर्णांक आदि) के एक मूल्य है, इसकी धीमी संदर्भ द्वारा इसे वापस करने के लिए (हालांकि कभी कभी संकलक इस अनुकूलन कर सकते हैं)। लेकिन बड़ा संरचनाओं के लिए एक संदर्भ/सूचक संकलक के लिए काम करते की राशि कम कर सकते हैं, और यह वास्तव में तेजी से अगर संकलक कुछ अस्थायी प्रतियां आदि बनाने से बचने के लिए सक्षम नहीं होगा हो सकता है

अद्यतन: ठीक है , यहाँ एक अधिक विशिष्ट उदाहरण है:

#include <stdio.h> 

int main(void) { 

    int a,b, i,j,s1,s2; 

    a = 123 + printf(""); // unknown in compile time 
    s1 = 1; 
    // bit reverse loop v1, gets unrolled 
    for(i=0; i<8; i++) { j = 1 << i; s1 += s1 + ((a&j)>0); } 
    s1 -= 256; 

    b = s1 + printf(""); 
    // bit reverse loop v2, not unrolled 
    for(s2=1; s2<256; s2+=s2+(j>0)) { j = b & s2; b -= j; } 
    s2 -= 256; 

    printf("a=%02X s1=%02X s2=%02X\n", a, s1, s2); 
} 

जीसीसी के लिए Asm लिस्टिंग/intelc यहां उपलब्ध हैं: http://nishi.dreamhosters.com/u/1.zip

+0

मानते हैं कि 'XXX' जटिल तरीकों से 'j' को संशोधित नहीं करता है, यह पूरी तरह से संभव है कि एक अनुकूलन कंपाइलर पुनरावृत्तियों की संख्या निर्धारित करने में सक्षम होगा दूसरा लूप –

+0

लूप का एक बेहतर उदाहरण जिसके लिए पुनरावृत्तियों की संख्या ज्ञात नहीं है (और इस प्रकार अनलॉक नहीं किया जा सकता है) एक लिंक्ड सूची पर पुनरावृत्त होगा: '(ptr_t p = first; p; p = p-> अगला) {'। कंपाइलर संभवतः पुनरावृत्तियों की संख्या को नहीं जान सकता है। दूसरे मामले में, जैसा कि बेन बताता है, ऑप्टिमाइज़र यह जान सकता है कि वह लूप 7 गुना होगा (जब तक 'XXX'' j' संशोधित नहीं होता)। +1 के लिए इस तथ्य को लेकर कि यह 'के लिए/नहीं' है लेकिन लूप के बारे में संकलक कितना जानता है। (एक और सरल उदाहरण: 'के लिए (int i = 0; i

4

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

2) वे नहीं करते हैं।

3) एक मूल्य लौटने के लिए अक्सर डेटा की एक प्रति की आवश्यकता होती है। यदि डेटा एक साधारण प्रकार है (जैसे int या float) तो यह एक रजिस्टर के माध्यम से वापस किया जा सकता है और वापस कर दिया जाएगा। यदि ऑब्जेक्ट किसी रजिस्टर में फ़िट होने के लिए बहुत बड़ा है तो कंपाइलर को ऑब्जेक्ट के लिए स्टैक पर स्थान आवंटित करना होगा और उसके बाद इस आवंटित स्थान में वापस आने वाले डेटा की प्रतिलिपि बनाना होगा। संदर्भ के रूप में मूल्य को पास करना, आमतौर पर, डेटा में सूचक को पास करके कार्यान्वित किया जाता है। इसलिए आप सीधे उस मेमोरी पते पर डेटा को संशोधित करके मान वापस कर देते हैं। कोई प्रतिलिपि नहीं होती है और इसलिए यह तेज़ है। ध्यान दें, हालांकि, Return Value Optimisation (RVO) का अर्थ यह हो सकता है कि संदर्भ के रूप में वापसी मूल्य को पारित करने के लिए कोई जीत नहीं है। समान रूप से, टिप्पणियों में बताया गया है, सी ++ 0x का नया चाल कन्स्ट्रक्टर आरवीओ के समान बोनस भी प्रदान कर सकता है।

असेंबलर उदाहरणों, आईएमओ का उपयोग करने वाले किसी भी व्यक्ति को समझाने की आवश्यकता नहीं है।

+1

ग्लोबल वैरिएबल आमतौर पर HEAP से आवंटित नहीं होते हैं (जब तक कि आप हेप द्वारा मेरे द्वारा किए गए शब्दों के लिए कुछ अलग न हों)। –

+1

@ चार्ल्स: अच्छा यह एक मजेदार है वास्तव में यह नहीं है। मैंने हमेशा (और सिखाया गया) माना है कि कुछ भी जो ढेर होने का ढेर नहीं है। उस आधार पर, ढेर को ढेर पर भी आवंटित किया जाएगा (जिसे मैं मानता हूं)। लेकिन एक तरह से आप सही हैं कि वैश्विक आवंटन से ढेर नहीं है। यह प्रक्रिया का आभासी पता स्थान है। – Goz

+2

"3) किसी मूल्य को लौटने के लिए डेटा की एक प्रति की आवश्यकता होती है" - हालांकि एक चाल के साथ नहीं। –

1

सबसे पहले आप एक लक्ष्य मंच निर्दिष्ट नहीं किया है, हाथ, 86, 6502, zpu, आदि

1) जब किसी फ़ंक्शन (सी/सी ++) के अंदर एक वैश्विक चर का उपयोग किया जाता है, तो क्या इसे सीधे रजिस्टरों या स्टैक से लिया जाएगा?

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

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

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

सीधे फ़ंक्शन में उपयोग किया जाता है, तो यह कोड/कंपाइलर/लक्ष्य पर निर्भर करता है कि वैश्विक को सीधे इसके निश्चित स्मृति स्थान से एक्सेस किया गया है या यदि कोई रजिस्टर उस स्मृति स्थान को लोड करता है और मान को किसी रजिस्टर से संचालित किया जाता है। इस मामले में ढेर का उपयोग नहीं किया जाता है, इसलिए उत्तर या तो (गैर-ढेर) स्मृति या पंजीकरण होता है।

2) क्यों लम्बे लूप (लूप के लिए) को महान लूप (जबकि लूप/करते समय) के अनुकूलन के लिए अधिक गुंजाइश माना जाता है?

कोड, कंपाइलर और लक्ष्य पर निर्भर करता है, मैं एक सामान्य मामले के बारे में नहीं सोच सकता जहां एक दूसरे से बेहतर है।

3) मूल्य वापस क्यों करना संदर्भ के आधार पर गुजरने के समान नहीं है?

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

1

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

एक सामान्य अनुकूलन (नामित) रिटर्न वैल्यू ऑप्टिमाइज़ेशन (एन) आरवीओ कहा जाता है कि संकलक अनावश्यक प्रतियों से बचने के लिए प्रदर्शन कर सकता है।

// RVO     // NRVO    // cannot perform RVO 
type foo() {   type bar() {  type baz() { 
    value a;    type a;    type a,b; 
    // operate on a   // modify a   // pass a and b to other functions 
    return type(a);   return a;   if (random() > x) return a; 
}      }      else return b; 
              } 

दोनों foo और bar में, संकलक कोड का विश्लेषण और पाते हैं कि वह foo में अस्थायी type(a) या नामित स्थानीय चर abarमें हैं समारोह की वापसी मान करने में सक्षम है, तो यह कर सकते हैं वापसी वस्तुओं (कॉलिंग सम्मेलन के अनुसार) के स्थान पर उन वस्तुओं का निर्माण करें और इसे कॉपी करने से बचें। तुलना करें कि baz के साथ जहां संकलक को वास्तव में यह जानना चाहिए कि कौन से को वापस लौटाया जाना है, पहले संकलक a और b बनाना चाहिए। इस मामले में संकलक कुछ भी अनुकूलित नहीं कर सकता है, ऑपरेशन करने के लिए और अंत में प्रति या तो a या b वापसी मूल्य पर प्रतिलिपि करना है।

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

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

कुछ विशेष मामले एक ऐसा कार्य हो सकते हैं जो वैक्टर बनाता है और उसे एक तंग लूप में बुलाया जाता है, जहां संदर्भ में पारित एक एकल वेक्टर होता है, जो फ़ंक्शन में साफ़ हो जाता है और फिर भरने से स्मृति आवंटन की संख्या कम हो जाती है (clear() एक वेक्टर स्मृति को निष्क्रिय नहीं करता है, इसलिए इसे अगले पुनरावृत्ति में पुन: आवंटित करने की आवश्यकता नहीं है)।

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

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