2009-11-13 22 views
9

द्वारा अभिन्न प्रकारों को पारित करने के क्या फायदे हैं प्रश्न: क्या केवल मूल्य के विपरीत कॉन्स्ट संदर्भ द्वारा एक अभिन्न प्रकार को पारित करने का लाभ है।कॉन्स रेफ

यानी।

void foo(const int& n); // case #1 

बनाम

void foo(int n); // case #2 

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

बहुत छोटे इनलाइन फ़ंक्शंस के लिए, क्या कंपाइलर को # 2 के मामले में पूर्णांक की एक प्रति बनाना होगा? कंपाइलर को यह बताकर हम संदर्भ को नहीं बदल पाएंगे, यह पूर्णांक की अनावश्यक प्रतिलिपि के बिना फ़ंक्शन कॉल को रेखांकित कर सकता है?

कोई सलाह स्वागत है।

उत्तर

12

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

हालांकि, अपने माध्यमिक प्रश्न के लिए:

बहुत छोटे इनलाइन कार्यों के लिए, संकलक # 2 मामले में पूर्णांक की एक प्रतिलिपि बनाने करना होगा? कंपाइलर को यह बताकर हम संदर्भ को नहीं बदल पाएंगे, यह पूर्णांक की अनावश्यक प्रतिलिपि के बिना फ़ंक्शन कॉल को रेखांकित कर सकता है?

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

4

यह आमतौर पर इसके लायक नहीं है। इनलाइन फ़ंक्शन के लिए भी, कंपाइलर बेवकूफ नहीं होगा। एकमात्र बार मैं कहूंगा कि यह उचित है यदि आपके पास टेम्पलेट था; यह किसी संदर्भ के बजाय प्रतिलिपि लेने के लिए बिल्टिन के लिए विशेषज्ञता के अतिरिक्त प्रयास के लायक नहीं हो सकता है।

1

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

6

मुझे वास्तव में यह परेशान लगता है जब कोई बुनियादी डेटाटाइप के लिए इस तरह के कॉन्स संदर्भों का उपयोग करता है। मुझे ऐसा करने का कोई फायदा नहीं दिख रहा है, हालांकि यह तर्क दिया जा सकता है कि sizeof(pointer) से अधिक डेटाटाइप के लिए यह अधिक कुशल हो सकता है। हालांकि, मुझे वास्तव में इस तरह के मिनट 'अनुकूलन' की परवाह नहीं है।

0

अच्छी तरह से एक संदर्भ की लागत एक अभिन्न प्रकार की समान होती है, लेकिन संदर्भ के साथ आपके पास एक संकेत है जो होना चाहिए, क्योंकि कुछ स्मृति के संदर्भ को एक मान में हल किया जाना चाहिए।

बस मूल्य से प्रतिलिपि बनाएँ, अंतर्निहित प्रकारों के लिए एक अपरिवर्तनीय सम्मेलन के साथ चिपके रहें।

7

यह कंपाइलर पर निर्भर करता है, लेकिन मुझे उम्मीद है कि कोई भी उचित अनुकूलक आपको वही परिणाम देगा।

मैंने जीसीसी के साथ परीक्षण किया, और परिणाम वास्तव में वही थे। यहाँ कोड मैं परीक्षण किया है:

inline int foo(const int& n) { 
    return n * 2; 
} 

int bar(int x) { 
    int y = foo(x); 
    return y; 
} 

(साथ और foo के n पैरामीटर पर स्थिरांक & के बिना)

मैं तो निम्न आदेश लाइन के साथ जीसीसी 4.0.1 के साथ संकलित:

g++ -O3 -S -o foo.s foo.cc 

दो संकलन के आउटपुट समान थे।

+0

+1 परीक्षण के लिए धन्यवाद। – DeusAduro

+0

मुझे आश्चर्य है कि क्या वे एक ही असेंबली आउटपुट होंगे यदि बार() दूसरी फाइल में था? शायद ऩही। – asveikau

+0

@asveikau: यदि फ़ंक्शन इनलाइन है, तो कोई अन्य तरीका नहीं है() किसी अन्य संकलन इकाई में हो सकता है। इनलाइन फ़ंक्शंस को हेडर फ़ाइल में परिभाषित किया जाना चाहिए, और सभी संकलन इकाइयों में उस शीर्षलेख को शामिल किया जाएगा, इस प्रकार संपूर्ण परिभाषा समेत। कीवर्ड 'इनलाइन' है और यह पूरे अंतर को बनाता है क्योंकि सभी कॉल साइटों पर कंपाइलर फ़ंक्शन के सटीक आंतरिक जानता है। –

1

ऐसा मत करें। int 64-बिट पर सामान्य 32-बिट प्लेटफॉर्म पर पॉइंटर/संदर्भ के समान आकार और छोटे पर आधारित है, इस प्रकार आप लाभ के बजाय स्वयं को एक प्रदर्शन नुकसान प्राप्त कर सकते हैं। मेरा मतलब है, सभी फ़ंक्शन तर्कों को क्रम में ढेर पर धक्का दिया जाता है ताकि एक फ़ंक्शन उन्हें पढ़ सके, और यह संदर्भ के मामले में आपका int या उसका पता होगा। एक और नुकसान यह है कि कैली या तो आपके n को एक संकेत (एक पते को डिफ्रेंसिंग) के माध्यम से एक्सेस करेगा, या यह ऑप्टिमाइज़ेशन के रूप में अपने ढेर पर प्रतिलिपि बनायेगा।

यदि आप मूल्य से गुजरने वाले इंट में कुछ बदलाव करते हैं, तो इसे या तो उस स्थान पर वापस लिखा जा सकता है जहां इसे पारित किया गया था, या एक नई स्टैक स्थिति पर। दूसरा मामला स्वाभाविक रूप से फायदेमंद नहीं है, लेकिन ऐसा नहीं होना चाहिए। इस तरह के बदलाव करने से आपको खुद को रोककर, लेकिन यह const int के साथ ही काम करेगा।

उचित इनलाइन मामले में इससे कोई फर्क नहीं पड़ता, स्वाभाविक रूप से, लेकिन ध्यान रखें कि जहां भी आप इनलाइन लिखते हैं, वह सब कुछ नहीं होगा।

2

टेम्पलेट्स लिखते या उपयोग करते समय, आप (कॉन्स int &) के साथ समाप्त हो सकते हैं क्योंकि टेम्पलेट लेखक यह नहीं जान सकता कि वास्तव में किस प्रकार का प्रकार है। यदि ऑब्जेक्ट हेवीवेट है, तो संदर्भ पास करना सही काम है; यदि यह एक int या कुछ है, तो संकलक इसे अनुकूलित करने में सक्षम हो सकता है।

किसी प्रकार की बाहरी आवश्यकता की अनुपस्थिति में, आमतौर पर एक-ऑफ फ़ंक्शन के लिए ऐसा करने का कोई कारण नहीं होता है - यह केवल अतिरिक्त टाइपिंग है, साथ ही संदर्भों के आसपास फेंकने से वास्तव में ऑप्टिमाइज़ेशन को रोकना पड़ता है। रजिस्टरों में छोटे डेटा की प्रतिलिपि इसे बदलने के मामले में स्मृति से पुनः लोड करने से कहीं अधिक सस्ता है!

2

बहुत से लोग कह रहे हैं कि दोनों के बीच कोई अंतर नहीं है। मैं एक (शायद काल्पनिक) मामला है जिसमें अंतर बात है ...

int i = 0; 

void f(const int &j) 
{ 
    i++; 

    if (j == 0) 
    {   
     // Do something. 
    } 
} 

void g() 
{ 
    f(i); 
} 

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

0

यह केवल प्रदर्शन नहीं है। एक सच्ची कहानी: इस सप्ताह मैंने देखा है कि एक सहयोगी न्यूमेरिकल व्यंजनों को बेहतर बनाने की कोशिश की और इस समारोह से मैक्रो


    #define SHFT(a,b,c,d) do { (a)=(b); (b)=(c); (c)=(d); } while (0) 

प्रतिस्थापित


    inline void Rotate(double& dFirst, double& dSecond, double& dThird, const double dNewValue) 
    { 
     dFirst = dSecond; 
     dSecond = dThird; 
     dThird = dNewValue; 
    } // Function Rotate 

यह काम किया है होता अगर वह पारित किया था संदर्भ द्वारा पिछले पैरामीटर है, लेकिन के रूप में यह है, इस कोड


     Rotate(dum,*fb,*fa,dum); 

जो चाहिए था स्वैप करने के लिए * पिता और * अब काम नहीं करता अमेरिकन प्लान। बिना किसी कॉन्स्ट के संदर्भ द्वारा इसे पास करना संभव नहीं है, क्योंकि अन्य स्थानों में गैर-एल-मान अंतिम पैरामीटर को पास किए जाते हैं।

2

आप इष्टतम पैरामीटर पास करने के लिए का उपयोग कर सकते हैं। जो कि आदिम प्रकारों से गुज़रने वाले सरल पैरामीटर के लिए डिफ़ॉल्ट है और structs और वर्गों के संदर्भ के संदर्भ में गुजरता है।

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