क्या यह कोड लिखने के लिए बुरा अभ्यास है जो संकलक अनुकूलन पर निर्भर करता है?


99

मैं कुछ C ++ सीख रहा हूं, और अक्सर फ़ंक्शन से निर्मित कार्यों से बड़ी वस्तुओं को वापस करना पड़ता है। मुझे पता है कि संदर्भ द्वारा पास है, एक पॉइंटर लौटाओ, और एक संदर्भ प्रकार के समाधान लौटाओ, लेकिन मैंने यह भी पढ़ा है कि C ++ कंपाइलर (और C ++ मानक) रिटर्न वैल्यू ऑप्टिमाइज़ेशन के लिए अनुमति देता है, जो मेमोरी के माध्यम से इन बड़ी वस्तुओं को कॉपी करने से बचता है, जिससे उस सभी का समय और स्मृति की बचत।

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

क्या यह एक माइक्रो-ऑप्टिमाइज़ेशन है, या मुझे अपना कोड डिज़ाइन करते समय कुछ ध्यान में रखना चाहिए?


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

4
मेगाबाइट्स के आदेश पर वस्तुएं काफी बड़ी होने पर भी @alfrat? जिन समस्याओं को मैं हल कर रहा हूं, उनकी प्रकृति के कारण मेरी सरणियाँ भारी हो सकती हैं।
मैट

6
@ मैट मैं नहीं होगा। संदर्भ / संकेत इसके लिए सटीक रूप से मौजूद हैं। कंपाइलर ऑप्टिमाइजेशन से परे माना जाता है कि प्रोग्राम बनाते समय प्रोग्रामर्स को क्या ध्यान रखना चाहिए, भले ही हाँ, अक्सर दो दुनिया ओवरलैप होती है।
नील

5
@ मैट जब तक आप कुछ बेहद विशिष्ट नहीं कर रहे हैं, जो कि सी / गुठली में 10 + ईश अनुभव वाले डेवलपर्स की आवश्यकता होती है, कम हार्डवेयर इंटरैक्शन जिनकी आपको आवश्यकता नहीं होनी चाहिए। यदि आपको लगता है कि आप कुछ बहुत विशिष्ट हैं, तो अपनी पोस्ट को संपादित करें और जो आप आवेदन कर रहे हैं उसका सटीक विवरण जोड़ें (वास्तविक समय? भारी गणित अभिकलन? ...)
Walfrat

37
C ++ के (N) RVO के विशेष मामले में, हाँ, इस अनुकूलन पर निर्भर होना पूरी तरह से मान्य है। ऐसा इसलिए है क्योंकि C ++ 17 मानक विशेष रूप से यह कहता है कि ऐसा होता है, उन स्थितियों में जो आधुनिक संकलक पहले से कर रहे थे।
केल

जवाबों:


130

कम से कम विस्मयकारी सिद्धांत को रोजगार दें ।

क्या यह आप और केवल आप ही हैं जो इस कोड का उपयोग करने जा रहे हैं, और क्या आप सुनिश्चित हैं कि 3 वर्षों में आप जो करेंगे उससे आश्चर्यचकित नहीं होंगे?

तो आगे बढ़ो।

अन्य सभी मामलों में, मानक तरीके का उपयोग करें; अन्यथा, आप और आपके सहयोगी बग ढूंढने के लिए कड़ी मेहनत करने वाले हैं।

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


88
@ यह मेरी बात है, हर कोई शॉर्ट-सर्किट मूल्यांकन पर निर्भर करता है। और आपको इसके बारे में दो बार नहीं सोचना चाहिए, इसे चालू करना चाहिए। यह एक डिफेक्टो मानक है। हां आप इसे बदल सकते हैं, लेकिन आपको ऐसा नहीं करना चाहिए।
पीटर बी

49
"मैंने बदल दिया कि भाषा कैसे काम करती है, और आपका गंदा सड़ा हुआ कोड टूट गया! अर्घ!" वाह। थप्पड़ मारना उचित होगा, अपने सहयोगी को ज़ेन प्रशिक्षण पर भेजें, वहाँ बहुत कुछ है।

109
@PieterB मुझे पूरा यकीन है कि C और C ++ भाषा चश्मा शॉर्ट-सर्किट मूल्यांकन की गारंटी देते हैं। तो यह सिर्फ एक वास्तविक मानक नहीं है, यह है मानक। इसके बिना, आप अब C / C ++ का उपयोग भी नहीं कर रहे हैं, लेकिन कुछ ऐसा है जो संदिग्ध रूप से पसंद कर रहा है: P
marcelm

47
बस संदर्भ के लिए, मानक तरीका यहाँ मूल्य द्वारा वापस लौटना है।
डेडएमजी ऑक्ट

28
@ dan04 हाँ यह डेल्फी में था। दोस्तों, यह मेरे द्वारा बनाए गए बिंदु के बारे में नहीं है। आश्चर्य की बात सामान मत करो कोई और करता है।
बी में Pieter B

81

इस विशेष मामले के लिए, निश्चित रूप से केवल मूल्य से वापस लौटें।

  • आरवीओ और एनआरवीओ अच्छी तरह से ज्ञात और मजबूत अनुकूलन हैं जो वास्तव में किसी भी सभ्य संकलक द्वारा बनाए जाने चाहिए, यहां तक ​​कि सी -1 03 मोड में भी।

  • मूवमेंट शब्दार्थ यह सुनिश्चित करता है कि यदि (N) RVO जगह नहीं ले रहा है तो ऑब्जेक्ट फ़ंक्शंस से बाहर चले जाएंगे। अपने वस्तु गतिशील डेटा का उपयोग करता है आंतरिक रूप से (जैसे यदि यह केवल उपयोगी है std::vectorकरता है), लेकिन वास्तव में ऐसा होना चाहिए अगर यह होता है कि ढेर बह निकला बड़ा स्वत: वस्तुओं के साथ एक जोखिम है - बड़ा।

  • सी ++ 17 आरवीओ को लागू करता है । तो चिंता मत करो, यह आप पर गायब नहीं होगा और केवल कंपाइलर्स अप-टू-डेट होने के बाद ही खुद को पूरी तरह से स्थापित करना समाप्त कर देगा।

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

बस कोड लिखें जो समझ में आता है और समझ में आने वाले कोड को सही ढंग से अनुकूलित करने के लिए संकलक लेखकों को धन्यवाद देता है।


9
बस मज़े के लिए देखें कि 1990-ईश से बोरलैंड टर्बो सी ++ 3.0 आरवीओ को कैसे संभालता है । Spoiler: यह मूल रूप से ठीक काम करता है।
nwp

9
यहाँ कुंजी यह है कि यह कुछ रैंडम कंपाइलर-विशिष्ट अनुकूलन या "अनडूस्ड फीचर" नहीं है, लेकिन ऐसा कुछ है, जबकि तकनीकी रूप से C ++ मानक के कई संस्करणों में वैकल्पिक रूप से, उद्योग द्वारा भारी धक्का दिया गया था और प्रत्येक प्रमुख कंपाइलर ने इसके लिए बहुत कुछ किया है बहुत ज्यादा समय।

7
यह अनुकूलन उतना मजबूत नहीं है जितना कोई व्यक्ति पसंद कर सकता है। हां, यह सबसे स्पष्ट मामलों में विश्वसनीय है, लेकिन जीसीसी के बुगज़िला में उदाहरण की तलाश में, कई मुश्किल से कम-स्पष्ट मामले हैं जहां यह याद किया जाता है।
मार्क ग्लिससे

62

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

यह कुछ ज्ञात नहीं है, cutesy, माइक्रो-ऑप्टिमाइज़ेशन जिसे आप कुछ छोटे, छोटे ट्रैफ़िक ब्लॉग के बारे में पढ़ते हैं और फिर आप उपयोग करने के बारे में चतुर और बेहतर महसूस करते हैं।

C ++ 11 के बाद, RVO इस कोड ऑफ़ कोड को लिखने का मानक तरीका है। यह आम है, अपेक्षित है, सिखाया गया है, बातचीत में उल्लेख किया गया है, ब्लॉग में उल्लेख किया गया है, मानक में उल्लेख किया गया है, लागू नहीं होने पर कंपाइलर बग के रूप में रिपोर्ट किया जाएगा। C ++ 17 में, भाषा एक कदम आगे बढ़ जाती है और कुछ परिदृश्यों में कॉपी एलिसन को अनिवार्य कर देती है।

आपको इस अनुकूलन पर पूरा भरोसा करना चाहिए।

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


3
धन्यवाद, यह बहुत कुछ समझ में आता है और ऊपर वर्णित "कम से कम आश्चर्यजनक सिद्धांत" के अनुरूप है। यह कोड को बहुत स्पष्ट और समझ में आता है, और सूचक के साथ गड़बड़ करने के लिए इसे कठिन बनाता है।
मैट

3
@Matt कारण मैंने इस उत्तर को दिया है कि यह "मूल्य शब्दार्थ" का उल्लेख करता है। जब आप C ++ (और सामान्य रूप से प्रोग्रामिंग) में अधिक अनुभव प्राप्त करते हैं, तो आपको कभी-कभी ऐसी परिस्थितियाँ मिलेंगी जहाँ कुछ वस्तुओं के लिए शब्दार्थ का उपयोग नहीं किया जा सकता है क्योंकि वे परिवर्तनशील हैं और उनके परिवर्तन को अन्य कोड के लिए दृश्यमान बनाने की आवश्यकता होती है जो उसी वस्तु का उपयोग करता है (एक "साझा परिवर्तनशीलता" का उदाहरण)। जब ये स्थितियां होती हैं, तो प्रभावित वस्तुओं को (स्मार्ट) पॉइंटर्स के माध्यम से साझा करने की आवश्यकता होगी।
21

16

आपके द्वारा लिखे गए कोड की शुद्धता कभी भी अनुकूलन पर निर्भर नहीं होनी चाहिए । C ++ "वर्चुअल मशीन" पर निष्पादित होने पर इसे सही परिणाम का उत्पादन करना चाहिए जो वे विनिर्देश में उपयोग करते हैं।

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

हालाँकि, यदि आपको इस अनुकूलन की आवश्यकता होती है (जैसे कि यदि कॉपी कंस्ट्रक्टर वास्तव में आपके कोड को विफल करने का कारण होगा), तो अब आप कंपाइलर के चक्कर में हैं।

मुझे लगता है कि मेरे अपने अभ्यास में इसका सबसे अच्छा उदाहरण टेल कॉल ऑप्टिमाइज़ेशन है:

   int sillyAdd(int a, int b)
   {
      if (b == 0)
          return a;
      return sillyAdd(a + 1, b - 1);
   }

यह एक मूर्खतापूर्ण उदाहरण है, लेकिन यह एक पूंछ कॉल को दर्शाता है, जहां एक फ़ंक्शन के अंत में एक फ़ंक्शन को पुनरावर्ती रूप से सही कहा जाता है। C ++ वर्चुअल मशीन दिखाएगा कि यह कोड ठीक से काम करता है, हालांकि मुझे इस बात का थोड़ा भ्रम हो सकता है कि मैंने पहली बार में इस तरह की अतिरिक्त दिनचर्या लिखने को क्यों परेशान किया। हालांकि, C ++ के व्यावहारिक कार्यान्वयन में, हमारे पास एक स्टैक है, और इसमें सीमित स्थान है। यदि पांडित्यपूर्ण रूप से किया जाता है, तो इस फ़ंक्शन को b + 1स्टैक पर कम से कम स्टैक फ़्रेम को धक्का देना होगा क्योंकि यह इसके अतिरिक्त करता है। अगर मैं गणना करना चाहता हूं sillyAdd(5, 7), तो यह कोई बड़ी बात नहीं है। अगर मैं गणना करना चाहता हूं sillyAdd(0, 1000000000), तो मैं स्टैकऑवरफ्लो (और अच्छी तरह नहीं ) पैदा करने की वास्तविक समस्या में हो सकता हूं ।

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

   int sillyAdd(int a, int b)
   {
      begin:
      if (b == 0)
          return a;
      // return sillyAdd(a + 1, b - 1);
      a = a + 1;
      b = b - 1;
      goto begin;  
   }

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

इस प्रकार यह मेरे लिए बुरा होगा कि मैं गणना करने में सक्षम होने पर निर्भर रहूं sillyAdd(0, 1000000000)


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

5
@sdenham मुझे लगता है कि तर्क में थोड़ी जगह है। यदि आप अब "C ++" के लिए नहीं लिख रहे हैं, बल्कि "WindRiver C ++ कंपाइलर संस्करण 3.4.1" के लिए लिख रहे हैं, तो मैं वहां तर्क देख सकता हूं। हालांकि, एक सामान्य नियम के रूप में, यदि आप कुछ ऐसा लिख ​​रहे हैं जो कल्पना के अनुसार ठीक से काम नहीं करता है, तो आप बहुत अलग तरह के परिदृश्य में हैं। मुझे पता है कि बूस्ट लाइब्रेरी में इस तरह का कोड होता है, लेकिन वे हमेशा इसे #ifdefब्लॉक में रखते हैं, और एक मानक वर्कअराउंड उपलब्ध है।
कॉर्ट अमोन

4
क्या यह कोड के दूसरे ब्लॉक में एक टाइपो है जहां यह कहता है b = b + 1?
स्टेब

2
आप "C ++ वर्चुअल मशीन" से क्या मतलब है, यह बतलाना चाहते हैं, क्योंकि यह किसी भी मानक दस्तावेज़ में प्रयुक्त शब्द नहीं है। मुझे लगता है कि आप C ++ के निष्पादन मॉडल के बारे में बात कर रहे हैं, लेकिन पूरी तरह से निश्चित नहीं है - और आपका शब्द भ्रामक रूप से एक "बाइटकोड वर्चुअल मशीन" के समान है जो कुछ पूरी तरह से अलग से संबंधित है।
टोबे स्पीट

1
@supercat Scala में स्पष्ट पूंछ पुनरावृत्ति वाक्यविन्यास भी है। C ++ अपना स्वयं का जानवर है, लेकिन मुझे लगता है कि पूंछ की पुनरावृत्ति गैर-कार्यात्मक भाषाओं के लिए अपरिभाषित है, और कार्यात्मक भाषाओं के लिए अनिवार्य है, भाषाओं का एक छोटा सा सेट छोड़कर जहां यह स्पष्ट पूंछ पुनरावृत्ति वाक्यविन्यास के लिए उचित है। लूप और स्पष्ट रूप से उत्परिवर्तन में पूंछ पुनरावृत्ति का अनुवाद करना कई भाषाओं के लिए बेहतर विकल्प है।
अभियोजन पक्ष

8

व्यवहार में C ++ प्रोग्राम कुछ संकलक अनुकूलन की उम्मीद कर रहे हैं।

अपने मानक कंटेनरों के मानक हेडर में विशेष रूप से देखें । GCC के साथ , आप कंटेनरों का उपयोग करके अधिकांश स्रोत फ़ाइलों (तकनीकी रूप से अनुवाद इकाइयों) के प्रीप्रोसेड फॉर्म ( g++ -C -E) और GIMPLE आंतरिक प्रतिनिधित्व ( g++ -fdump-tree-gimpleया Gimple SSA -fdump-tree-ssa) के लिए पूछ सकते हैं । आप अनुकूलन की मात्रा से आश्चर्यचकित होंगे जो (साथ g++ -O2) किया जाता है । इसलिए कंटेनरों के प्रवर्तक अनुकूलन पर भरोसा करते हैं (और ज्यादातर समय, C ++ मानक पुस्तकालय के कार्यान्वयनकर्ता जानते हैं कि अनुकूलन क्या होगा और उन लोगों के साथ कंटेनर कार्यान्वयन को ध्यान में रखकर लिखना होगा; कभी-कभी वह कंपाइलर में ऑप्टिमाइज़ेशन पास भी लिखेंगे; मानक C ++ लाइब्रेरी द्वारा आवश्यक सुविधाओं के साथ सौदा)।

व्यवहार में, यह संकलक अनुकूलन है जो C ++ और इसके मानक कंटेनरों को पर्याप्त रूप से कुशल बनाता है। इसलिए आप उन पर भरोसा कर सकते हैं।

और इसी तरह आपके प्रश्न में उल्लिखित RVO मामले के लिए।

C ++ मानक को संभव अनुकूलन के साथ अच्छी तरह से काम करने के लिए (नई सुविधाओं का प्रस्ताव करते समय अच्छी पर्याप्त अनुकूलन का प्रयोग करके) विशेष रूप से सह-डिज़ाइन किया गया था।

उदाहरण के लिए, नीचे दिए गए कार्यक्रम पर विचार करें:

#include <algorithm>
#include <vector>

extern "C" bool all_positive(const std::vector<int>& v) {
  return std::all_of(v.begin(), v.end(), [](int x){return x >0;});
}

इसके साथ संकलित करें g++ -O3 -fverbose-asm -S। आपको पता चलेगा कि उत्पन्न फ़ंक्शन किसी भी CALLमशीन निर्देश को नहीं चलाता है । इसलिए अधिकांश सी ++ स्टेप्स (लंबोदर क्लोजर का निर्माण, इसके बार-बार किए गए एप्लिकेशन, beginऔर endचलने वाले, आदि ...) को ऑप्टिमाइज़ किया गया है। मशीन कोड में केवल एक लूप होता है (जो स्रोत कोड में स्पष्ट रूप से प्रकट नहीं होता है)। ऐसे अनुकूलन के बिना, C ++ 11 सफल नहीं होगा।

परिशिष्ट

(जोड़ा दिसम्बर 31 सेंट 2017)

CppCon 2017 देखें : मैट गॉडबोल्ट "मेरे साथी ने मेरे लिए क्या किया? संकलक की ढक्कन को खोलना "" बात है।


4

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

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

कंपाइलर्स को कुशल आउटपुट देने के लिए बहुत प्रयास किया जाता है, और स्पष्ट रूप से उन क्षमताओं का उपयोग करने का इरादा है।

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


3

मुझे लगता है कि दूसरों ने सी ++ और आरवीओ के बारे में विशिष्ट कोण को अच्छी तरह से कवर किया। यहाँ एक अधिक सामान्य उत्तर दिया गया है:

जब शुद्धता की बात आती है, तो आपको सामान्य तौर पर संकलक अनुकूलन, या संकलक-विशिष्ट व्यवहार पर भरोसा नहीं करना चाहिए। सौभाग्य से, आप ऐसा नहीं करते हैं।

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


1

कंपाइलर ऑप्टिमाइज़ेशन केवल प्रदर्शन को प्रभावित करना चाहिए, परिणाम नहीं। गैर-कार्यात्मक आवश्यकताओं को पूरा करने के लिए संकलक अनुकूलन पर भरोसा करना न केवल उचित है, बल्कि यह अक्सर यही कारण है कि एक संकलक को दूसरे पर उठाया जाता है।

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

यदि कोई कंपाइलर ऑप्टिमाइज़ेशन अलग परिणाम देता है, तो वह एक बग है - कंपाइलर में एक बग। कंपाइलर में एक बग पर भरोसा करना, दीर्घकालिक गलती है - जब यह ठीक हो जाता है तो क्या होता है?

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


दुर्भाग्य से, बहुत सारे संकलक दस्तावेज यह निर्दिष्ट करने का खराब काम करते हैं कि विभिन्न मोड में क्या गारंटी है या नहीं है। इसके अलावा, "आधुनिक" संकलक लेखक गारंटी के संयोजन से अनजान लगते हैं जो प्रोग्रामर करते हैं और जिनकी आवश्यकता नहीं होती है। यदि एक कार्यक्रम ठीक काम करेगा अगर x*y>zअतिप्रवाह के मामले में मनमाने ढंग से 0 या 1 पैदावार देता है, बशर्ते कि इसका कोई अन्य दुष्प्रभाव न हो , इसके लिए यह आवश्यक है कि एक प्रोग्रामर को या तो हर कीमत पर ओवरफ्लो को रोकना होगा या कंपाइलर को अभिव्यक्ति का मूल्यांकन करने के लिए मजबूर करना होगा। अनावश्यक
हानि

... संकलक अपने अवकाश पर व्यवहार कर सकता है , हालांकि x*yइसके ऑपरेंड को कुछ मनमाने तरीके से टाइप करने के लिए प्रेरित करता है (इस प्रकार उत्थापन और शक्ति में कमी की अनुमति देता है जो कुछ अतिप्रवाह मामलों के व्यवहार को बदल देगा)। कई कंपाइलरों को, हालांकि, प्रोग्रामर को या तो हर कीमत पर ओवरफ्लो को रोकने की जरूरत होती है या ओवरले के मामले में सभी इंटरमीडिएट वैल्यूज़ को कम करने के लिए कंपाइलर्स को मजबूर करना पड़ता है।
सुपरकैट

1

नहीं।

यही मैं हर समय करता हूं। यदि मुझे स्मृति में एक मनमाना 16 बिट ब्लॉक तक पहुंचने की आवश्यकता है, तो मैं यह करता हूं

void *ptr = get_pointer();
uint16_t u16;
memcpy(&u16, ptr, sizeof(u16)); // ntohs omitted for simplicity

... और कोड के उस टुकड़े को अनुकूलित करने के लिए जो कुछ भी कर सकते हैं, उस पर भरोसा करें। कोड एआरएम, i386, एएमडी 64 पर काम करता है, और व्यावहारिक रूप से हर एक आर्किटेक्चर पर बाहर है। सिद्धांत रूप में, एक गैर-अनुकूलन कंपाइलर वास्तव में कॉल कर सकता है memcpy, जिसके परिणामस्वरूप पूरी तरह से खराब प्रदर्शन होता है, लेकिन यह मेरे लिए कोई समस्या नहीं है, क्योंकि मैं कंपाइलर अनुकूलन का उपयोग करता हूं।

विकल्प पर विचार करें:

void *ptr = get_pointer();
uint16_t *u16ptr = ptr;
uint16_t u16;
u16 = *u16ptr;  // ntohs omitted for simplicity

यह वैकल्पिक कोड उन मशीनों पर काम करने में विफल रहता है जिनके लिए get_pointer()एक गैर-संरेखित सूचक लौटाता है , जो उचित संरेखण की आवश्यकता होती है । इसके अलावा, विकल्प में अलियासिंग मुद्दे हो सकते हैं।

memcpyचाल का उपयोग करते समय -O2 और -O0 के बीच का अंतर महान है: IP चेकसम प्रदर्शन के 3.2 Gbps बनाम IP चेकसम प्रदर्शन के 67 Gbps। परिमाण अंतर के एक आदेश पर!

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

कंपाइलर ऑप्टिमाइज़ेशन पर भरोसा करने का दोष यह है कि यदि आप अपने कोड को डीबग करने के लिए gdb चलाते हैं, तो आपको पता चल सकता है कि बहुत कुछ ऑप्टिमाइज़ हो गया है। इसलिए, आपको -O0 के साथ पुनर्मिलन करने की आवश्यकता हो सकती है, जिसका अर्थ है कि डिबगिंग के दौरान प्रदर्शन पूरी तरह से चूसना होगा। मुझे लगता है कि यह अनुकूलन योग्यताओं के लाभों को देखते हुए, यह एक कमी है।

आप जो भी करते हैं, कृपया सुनिश्चित करें कि आपका रास्ता वास्तव में अपरिभाषित व्यवहार नहीं है। 16-बिट पूर्णांक के रूप में निश्चित रूप से मेमोरी के कुछ यादृच्छिक ब्लॉक तक पहुंच बनाना अलियासिंग और संरेखण मुद्दों के कारण अपरिभाषित व्यवहार है।


0

कुशल कोड पर सभी प्रयास, लेकिन असेंबली बहुत निर्भर करता है, कंपाइलर ऑप्टिमाइज़ेशन पर बहुत अधिक निर्भर करता है, सुपरफ़्लस स्टैक से बचने के लिए कुशल रजिस्टर आवंटन जैसे सबसे बुनियादी के साथ शुरू होता है और यदि उत्कृष्ट, निर्देश चयन नहीं तो कम से कम यथोचित रूप से अच्छा है। अन्यथा हम 80 के दशक में वापस आएँगे जहाँ हमें registerजगह-जगह पर संकेत लगाने थे और एक समारोह में कम से कम संख्या में चर का उपयोग करने के लिए पुरातन C कंपाइलर या इससे पहले भी जब gotoएक उपयोगी ब्रांचिंग अनुकूलन था।

यदि हमें ऐसा नहीं लगता था कि हम अपने कोड को ऑप्टिमाइज़र करने के लिए अपने ऑप्टिमाइज़र की क्षमता पर भरोसा कर सकते हैं, तो हम सभी अभी भी असेंबली में निष्पादन-महत्वपूर्ण निष्पादन पथ कोडिंग करेंगे।

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

आरवीओ एक ऐसी चीज है जो उम्र भर के लिए रही है, और, कम से कम बहुत जटिल मामलों को छोड़कर, कुछ कंपाइलर उम्र के लिए मज़बूती से लागू होते हैं। यह निश्चित रूप से एक समस्या के आसपास काम करने लायक नहीं है जो मौजूद नहीं है।

ऑप्टिमाइज़र पर भरोसा करने के पक्ष में एर, यह डर नहीं

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

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

प्रोफाइलिंग

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


-1

सॉफ़्टवेयर को C ++ में बहुत अलग प्लेटफार्मों पर और बहुत सारे विभिन्न उद्देश्यों के लिए लिखा जा सकता है।

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


-2

मुझे लगता है कि इसका उबाऊ उत्तर है: 'यह निर्भर करता है'।

क्या यह कोड लिखने के लिए बुरा व्यवहार है जो एक कंपाइलर ऑप्टिमाइज़ेशन पर निर्भर करता है जो बंद होने की संभावना है और जहां भेद्यता का दस्तावेजीकरण नहीं किया गया है और जहां प्रश्न में कोड का परीक्षण नहीं किया गया है ताकि अगर यह टूट जाए तो आपको पता चल जाए ? शायद।

क्या यह कोड लिखने के लिए बुरा व्यवहार है जो एक संकलक अनुकूलन पर निर्भर करता है जिसे बंद करने की संभावना नहीं है , जो कि प्रलेखित है और इकाई परीक्षण है ? शायद नहीं।


-6

जब तक अधिक नहीं है आप हमें नहीं बता रहे हैं, यह बुरा अभ्यास है, लेकिन आपके सुझाव के कारण नहीं।

संभवतः आपके द्वारा पहले उपयोग की गई अन्य भाषाओं के विपरीत, C ++ में किसी ऑब्जेक्ट का मान लौटाने से ऑब्जेक्ट की कॉपी प्राप्त होती है। आप तो वस्तु को संशोधित करते हैं, तो आप एक संशोधित कर रहे हैं विभिन्न वस्तु । यही कारण है, अगर मेरे पास है Obj a; a.x=1;और Obj b = a;है, तो मुझे क्या करना b.x += 2; b.f();है, तो a.xअभी भी 1 के बराबर होती है, नहीं 3।

तो नहीं, किसी ऑब्जेक्ट को संदर्भ या पॉइंटर के बजाय मान के रूप में उपयोग करने से समान कार्यक्षमता प्रदान नहीं होती है और आप अपने सॉफ़्टवेयर में बग्स को समाप्त कर सकते हैं।

शायद आप यह जानते हैं और यह आपके विशिष्ट उपयोग के मामले को नकारात्मक रूप से प्रभावित नहीं करता है। हालाँकि, आपके प्रश्न में शब्दांकन के आधार पर, यह प्रतीत होता है कि आप भेद के बारे में नहीं जानते होंगे; "कार्य में एक वस्तु बनाएं" जैसे शब्द।

"फ़ंक्शन में ऑब्जेक्ट बनाएँ" लगता है जैसे new Obj;"मान द्वारा ऑब्जेक्ट वापस करें" जैसा लगता हैObj a; return a;

Obj a;और Obj* a = new Obj;बहुत, बहुत अलग चीजें हैं; पूर्व का परिणाम मेमोरी भ्रष्टाचार में हो सकता है यदि ठीक से उपयोग नहीं किया जाता है और समझा जाता है, और बाद में मेमोरी लीक हो सकता है यदि ठीक से उपयोग नहीं किया जाता है और समझा जाता है।


8
रिटर्न वैल्यू ऑप्टिमाइज़ेशन (आरवीओ) एक अच्छी तरह से परिभाषित शब्दार्थ है जहां कंपाइलर स्टैक फ्रेम पर एक लौटे ऑब्जेक्ट का स्तर बनाता है, विशेष रूप से अनावश्यक ऑब्जेक्ट कॉपी से परहेज करता है। यह अच्छी तरह से परिभाषित व्यवहार है जो C ++ 17 में अनिवार्य होने से बहुत पहले समर्थित है। 10-15 साल पहले भी, सभी प्रमुख संकलक इस सुविधा का समर्थन करते थे और लगातार ऐसा करते थे।

@Snowman मैं भौतिक, निम्न-स्तरीय स्मृति प्रबंधन के बारे में बात नहीं कर रहा हूं, और मैंने मेमोरी ब्लोट या गति पर चर्चा नहीं की। जैसा कि मैंने विशेष रूप से अपने उत्तर में दिखाया है, मैं तार्किक डेटा के बारे में बात कर रहा हूं। तार्किक रूप से , किसी वस्तु के मूल्य की आपूर्ति, इसकी एक प्रति तैयार कर रही है, इस बात की परवाह किए बिना कि संकलक को कैसे लागू किया जाता है या पर्दे के पीछे किस असेंबली का उपयोग किया जाता है। निम्न-स्तरीय दृश्यों के पीछे एक चीज है, और भाषा की तार्किक संरचना और व्यवहार एक और है; वे संबंधित हैं, लेकिन वे एक ही चीज नहीं हैं - दोनों को समझा जाना चाहिए।
आरोन

6
आपका उत्तर कहता है "C ++ में किसी ऑब्जेक्ट का मान लौटाने से ऑब्जेक्ट की एक प्रति प्राप्त होती है" जो कि आरवीओ के संदर्भ में पूरी तरह से गलत है - ऑब्जेक्ट को सीधे कॉलिंग स्थान पर बनाया गया है, और कोई भी कॉपी कभी नहीं बनाई गई है। आप कॉपी कंस्ट्रक्टर को हटाकर और उस वस्तु का निर्माण कर सकते हैं, जो returnबयान में निर्मित है, जो आरवीओ के लिए एक आवश्यकता है। इसके अलावा, आप तब कीवर्ड newऔर पॉइंटर्स के बारे में बात करते हैं , जो आरवीओ के बारे में नहीं है। मेरा मानना ​​है कि आप या तो प्रश्न, या आरवीओ, या संभवतः दोनों को नहीं समझते हैं।

-7

कम से कम विस्मय की सिफारिश करने में पीटर बी बिल्कुल सही है।

आपके विशिष्ट प्रश्न का उत्तर देने के लिए, C ++ में इसका क्या अर्थ है (सबसे अधिक संभावना है) यह है कि आपको std::unique_ptrनिर्माण की गई वस्तु को वापस करना चाहिए ।

कारण यह है कि यह सी ++ डेवलपर के लिए स्पष्ट है कि क्या चल रहा है।

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

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


जब रोम में हो तो वैसा ही करो जैसा की रोमन करते हैं।

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

9
@Matt मामले में आपको महसूस नहीं हुआ कि यह सबसे अच्छा अभ्यास नहीं है। अनावश्यक रूप से मेमोरी एलोकेशन करना और यूजर्स पर पॉइंटर शब्दार्थ डालना बुरा है।
nwp

5
सबसे पहले, स्मार्ट पॉइंटर्स का उपयोग करते समय, एक को वापस लौटना चाहिए std::make_unique, std::unique_ptrसीधे नहीं । सभी में से, आरवीओ कुछ गूढ़, विक्रेता-विशिष्ट अनुकूलन नहीं है: यह मानक में बेक किया गया है। यहां तक ​​कि जब यह नहीं था, तो यह व्यापक रूप से समर्थित और अपेक्षित व्यवहार था। ऐसा कोई बिंदु नहीं है std::unique_ptrजब पहली जगह में एक पॉइंटर की आवश्यकता नहीं होती है।

4
@Snowman: कोई "जब यह नहीं था" नहीं है। यद्यपि यह हाल ही में अनिवार्य हो गया है , हर C ++ मानक ने कभी भी [एन] आरवीओ को मान्यता दी है, और इसे सक्षम करने के लिए जगह बनाई है (उदाहरण के लिए, कंपाइलर को रिटर्न वैल्यू पर कॉपी कंस्ट्रक्टर के उपयोग को छोड़ देने की स्पष्ट अनुमति दी गई है, भले ही यह हो दृश्य प्रभाव है)।
जेरी कॉफिन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.