2008-10-26 12 views
26

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

मान लें इस विधि सैकड़ों बार एक दूसरे ही कहा जाता है ...

मामले में मैं स्पष्ट नहीं किया जा रहा हूँ, यहाँ कुछ त्वरित उदाहरण है:

// use local variables 
class thisClass { 
    public: 
     void callback(msg& msg) 
     { 
      int varA; 
      double varB; 
      std::string varC; 
      varA = msg.getInt(); 
      varB = msg.getDouble(); 
      varC = msg.getString(); 

      // do a bunch of calculations 
     } 

}; 

// use member variables 
class thisClass { 
    public: 
     void callback(msg& msg) 
     { 
      m_varA = msg.getInt(); 
      m_varB = msg.getDouble(); 
      m_varC = msg.getString(); 

      // do a bunch of calculations 
     } 

    private: 
     int m_varA; 
     double m_varB; 
     std::string m_varC; 

}; 
+0

कोई अंतर लगभग निश्चित रूप से मूल्यों को पुनर्प्राप्त करने की लागत से निश्चित रूप से अस्पष्ट हो जाएगा। और एक सेकंड में कुछ सौ बार कुछ भी नहीं है। स्टैक फ्रेम की लागत * कॉल * आपके फ़ंक्शन को आपके द्वारा दिखाए गए कुछ भी बौने की संभावना होगी। समयपूर्व अनुकूलन की तरह बदबू आ रही है। – Shog9

+0

पहले से ही डाउनवॉटेड - कठिन भीड़ आज रात – fizzer

उत्तर

38

कार्यकारी सारांश: लगभग सभी परिदृश्यों में, इससे कोई फर्क नहीं पड़ता, लेकिन स्थानीय चर के लिए थोड़ा सा फायदा है।

चेतावनी: आप सूक्ष्म अनुकूलन कर रहे हैं। आप नैनोसेकंद जीतने वाले कोड को समझने की कोशिश कर रहे घंटों का समय समाप्त कर देंगे।

चेतावनी: आपके परिदृश्य में, प्रदर्शन प्रश्न नहीं होना चाहिए, लेकिन चर की भूमिका - क्या वे अस्थायी हैं, या इस क्लास की स्थिति हैं?

चेतावनी: अनुकूलन का पहला, दूसरा और अंतिम नियम: उपाय!


सबसे पहले, 86 के लिए उत्पन्न ठेठ विधानसभा को देखो (अपने मंच भिन्न हो सकते हैं):

// stack variable: load into eax 
mov eax, [esp+10] 

// member variable: load into eax 
mov ecx, [adress of object] 
mov eax, [ecx+4] 

एक बार वस्तु का पता भरी हुई है, एक रजिस्टर int, निर्देश समान हैं । ऑब्जेक्ट एड्रेस लोड करना आमतौर पर पहले के निर्देश के साथ जोड़ा जा सकता है और निष्पादन समय नहीं मारा जाता है।

लेकिन इसका मतलब है कि पारिस्थितिकी रजिस्टर अन्य अनुकूलन के लिए उपलब्ध नहीं है। हालांकि, आधुनिक CPUs कुछ कम समस्या बनाने के लिए कुछ तीव्र चालबाजी करते हैं।

इसके अलावा, कई वस्तुओं तक पहुंचने पर आपको अतिरिक्त खर्च हो सकता है। हालांकि, यह एक चक्र से कम औसत है, और निर्देशों को जोड़ने के लिए अक्सर अधिक अवसर होते हैं।

मेमोरी इलाके: यहां बड़े पैमाने पर जीतने के लिए ढेर का मौका है। ढेर का शीर्ष लगभग हमेशा एल 1 कैश में होता है, इसलिए भार एक चक्र लेता है। ऑब्जेक्ट को एल 2 कैश (अंगूठे का नियम, 10 चक्र) या मुख्य स्मृति (100 चक्र) पर वापस धकेलने की अधिक संभावना है।

हालांकि, आप इसे केवल पहली पहुंच के लिए भुगतान करते हैं। यदि आपके पास केवल एक ही पहुंच है, तो 10 या 100 चक्र अनजान हैं। यदि आपके पास हजारों पहुंच हैं, तो ऑब्जेक्ट डेटा भी एल 1 कैश में होगा।

संक्षेप में, लाभ इतना छोटा है कि यह बेहतर प्रदर्शन प्राप्त करने के लिए स्थानीय चर में सदस्य चर की प्रतिलिपि बनाने के लिए लगभग कभी समझ में नहीं आता है।

+1

सी ++ में कोई विशिष्ट एबीआई नहीं है। लेकिन मैंने जो पढ़ा है वह हमेशा इस सूचक के लिए आरक्षित है। तो कोई लाभ नहीं। इसके अलावा आप स्थानीय लोगों के अंदर-बाहर/बाहर की लागत को भूल जाते हैं। –

+0

आप सही हैं, इन चीजों का भी उल्लेख किया जाना चाहिए। – peterchen

+0

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

3

यह आपके compilers समस्या नहीं होनी चाहिए। इसके बजाए, रखरखाव के लिए अनुकूलित करें: यदि जानकारी केवल स्थानीय रूप से उपयोग की जाती है, तो इसे स्थानीय (स्वचालित) चर में संग्रहीत करें। मुझे सदस्य चर के साथ लिखे गए वर्गों को पढ़ने से नफरत है जो वास्तव में कक्षा के बारे में मुझे कुछ भी नहीं बताते हैं, लेकिन कुछ तरीकों के बारे में कुछ विवरण कैसे काम करते हैं :(

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

0

कब संदेह में, बेंचमार्क और खुद के लिए देखें। और सुनिश्चित करें कि यह पहले एक अंतर बनाता है - एक दूसरे प्रोसेसर पर सैकड़ों बार एक बड़ा बोझ नहीं है।

उसने कहा, मुझे नहीं लगता कि वहां होगा कोई अंतर। दोनों एक सूचक से निरंतर ऑफसेट होंगे, स्थानीय लोग स्टैक पॉइंटर से होंगे और सदस्य "इस" सूचक से होंगे।

0

सदस्य चर का उपयोग करना थोड़ा तेज़ होना चाहिए क्योंकि कॉलबैक लागू होने के बजाए उन्हें केवल एक बार आवंटित किया जाना चाहिए (जब वस्तु का निर्माण होता है)। लेकिन बाकी काम की तुलना में आप शायद कर रहे हैं, मुझे उम्मीद है कि यह एक बहुत ही छोटा प्रतिशत होगा। दोनों को बेंचमार्क करें और देखें कि तेज़ क्या है।

5

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

1

मेरी राय में, यह प्रभावित नहीं करना चाहिए प्रदर्शन, क्योंकि:

  • आपका पहला उदाहरण में, चर ढेर पर, उदाहरण के लिए एक देखने के माध्यम से पहुंचा जा सकता है [ईएसपी] +4 जिसका अर्थ है स्टैक प्लस चार बाइट्स का वर्तमान अंत।
  • दूसरे उदाहरण में, वैरिएबल को इसके सापेक्ष एक लुकअप के माध्यम से एक्सेस किया जाता है (याद रखें, varB>> varB के बराबर है)। यह एक समान मशीन निर्देश है।

इसलिए, इसमें कोई फर्क नहीं पड़ता है।

हालांकि, आपको स्ट्रिंग की प्रतिलिपि से बचने चाहिए;)

4

बेवकूफ सवाल।
यह सब संकलक पर निर्भर करता है और यह अनुकूलन के लिए क्या करता है।

भले ही यह काम करता है जो आपने प्राप्त किया है? अपना कोड obfuscate करने के लिए रास्ता?

परिवर्तनीय पहुंच आमतौर पर एक सूचक और ऑफसेट के माध्यम से किया जाता है। वस्तु के लिए +

  • सूचक ऑफसेट
  • सूचक फ्रेम स्टैक + ऑफसेट

इसके अलावा करने के लिए वापस स्थानीय संग्रहण में चर चलती है और फिर कॉपी करने के परिणाम की लागत में जोड़ने के लिए भूल नहीं है । जिनमें से सभी का अर्थ कम हो सकता है क्योंकि संकलक किसी भी तरह से इसे दूर करने के लिए पर्याप्त स्मार्ट हो सकता है।

+0

स्थानीय स्टोरेज में चर चलने की लागत का क्या मतलब है और फिर परिणामों को कॉपी करना? यह सवाल का हिस्सा है ... क्या स्थानीय चर के बजाय सदस्य चरों में मूल्यों की प्रतिलिपि बनाने में कोई प्रदर्शन लाभ है? –

0

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

+0

उसे वही व्यवहार प्राप्त करने के लिए हर बार उन्हें प्रारंभ करना होगा। और स्थानीय चर के लिए "आवंटन" एक अलग बेक्ड-इन स्टैक पॉइंटर वृद्धि में है। तो किसी भी तरह से, लागत प्रारंभिक लागत है। – Shog9

1

आपके द्वारा बातचीत की जा रही डेटा की मात्रा एल्गोरिदम के कार्यान्वयन में डेटा का प्रतिनिधित्व करने के तरीके की तुलना में निष्पादन गति पर एक बड़ा प्रभाव डालेगी।

प्रोसेसर वास्तव में परवाह नहीं करता है कि डेटा स्टैक पर या ढेर पर है (इसके अलावा स्टैक के शीर्ष प्रोसेसर कैश में पेटरचेन के रूप में होगा) लेकिन अधिकतम गति के लिए, डेटा होगा प्रोसेसर के कैश में फिट होना होगा (एल 1 कैश यदि आपके पास कैश के एक से अधिक स्तर हैं, जो कि सभी आधुनिक प्रोसेसर के पास है)। एल 2 कैश से कोई भी लोड - या $ DEITY forbid, मुख्य मेमोरी - निष्पादन को धीमा कर देगा। तो यदि आप एक स्ट्रिंग को संसाधित कर रहे हैं जो आकार में कुछ सौ केबी है और प्रत्येक आमंत्रण पर संभावना है, तो अंतर भी मापने योग्य नहीं होगा।

ध्यान रखें कि ज्यादातर मामलों में, एक प्रोग्राम में 10% की गति अंतिम उपयोगकर्ता के लिए बहुत अधिक ज्ञानी नहीं है (जब तक कि आप अपने रातोंरात बैच के रनटाइम को 25h से 24h से कम तक कम करने के लिए प्रबंधित नहीं करते) तो यह है जब तक आप सुनिश्चित न हों कि प्रोफाइल का यह विशेष भाग 10% -20% 'गर्म क्षेत्र' के भीतर है, जिसका आपके प्रोग्राम के रनटाइम पर बड़ा प्रभाव पड़ता है, तब तक परेशान करने के लायक नहीं है।

अन्य विचारों को बनाए रखना चाहिए जैसे रखरखाव या अन्य बाहरी कारक। उदाहरण के लिए यदि उपरोक्त कोड भारी मल्टीथ्रेड कोड में है, तो स्थानीय चर का उपयोग करके कार्यान्वयन आसान हो सकता है।

1

यह निर्भर करता है, लेकिन मुझे उम्मीद है कि बिल्कुल कोई अंतर नहीं होगा।

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

+0

पुन: प्रवेश! = समवर्ती। आप एक भ्रामक "यानी" से जुड़े दो सच्चे बयान देते हैं। गैर-पुन: प्रवेश कोड भी एकल-थ्रेडेड कोड में गलत हो सकता है, उदाहरण के लिए यदि कॉलबैक कुछ ऐसा कहता है जो इसे दोबारा कॉल करता है, या अगर इसे सिग्नल हैंडलर से बुलाया जाता है जिसने इसे बाधित कर दिया है। वैसे भी +1। –

+0

दोनों को उलझाने में समस्या यह है कि कभी-कभी बेवकूफ लोग सोचते हैं कि ताले जोड़कर, या केवल एक धागा करके, वे अपना कोड फिर से प्रवेश कर सकते हैं। ऐसा नहीं: यह केवल इसे समेकित सुरक्षित बनाता है, और पुन: प्रवेश के लिए अन्य कारण भी हैं। –

+0

"पुन: प्रवेश! = समरूपता"। तुम सही हो - धन्यवाद। मैंने 'i.e' को 'उदाहरण के लिए' बदल दिया है। – Roddy

1
कुछ अंक है कि दूसरों के द्वारा स्पष्ट रूप से उल्लेख नहीं किया गया है

:

  • आप संभवतः अपने कोड में काम ऑपरेटरों लागू कर रहे हैं। जैसे varC = msg.getString();

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

  • स्थानीय लोगों को कॉन्स्ट-रेफ होने की घोषणा करें और, निश्चित रूप से, उन्हें प्रारंभ करें।

  • सदस्य चर ढेर पर हो सकते हैं (यदि आपका ऑब्जेक्ट आवंटित किया गया था) और इसलिए गैर-इलाके से पीड़ित हैं।

  • यहां तक ​​कि कुछ चक्र बचाए गए भी अच्छे हैं - क्यों आप इसे टालने से बच सकते हैं गणना गणना समय।

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