क्या C ++ 11 के 'ऑटो' के उपयोग से प्रदर्शन में सुधार हो सकता है?


230

मैं देख सकता हूं कि autoC ++ 11 का प्रकार शुद्धता और रखरखाव में सुधार क्यों करता है । मैंने पढ़ा है कि यह प्रदर्शन में सुधार भी कर सकता है ( ऑलवेज ऑलवेज ऑटो बाय हर्ब सटर), लेकिन मुझे एक अच्छी व्याख्या याद आती है।

  • कैसे autoबेहतर हो सकता है प्रदर्शन?
  • क्या कोई उदाहरण दे सकता है?

5
जड़ी-बूटियों के डाट.कॉम देखें। 2013/06/13 जो आकस्मिक आकस्मिक रूपांतरणों से बचने की बात करता है, जैसे गैजेट से विजेट। यह कोई आम समस्या नहीं है।
जोनाथन वेकली

42
क्या आप स्वीकार करते हैं "प्रदर्शन में सुधार के रूप में यह अनायास ही कम संभावना है"?
5gon12eder

1
भविष्य में केवल कोड सफाई का सही उपयोग, हो सकता है
क्रॉल

हमें एक संक्षिप्त उत्तर चाहिए: यदि आप अच्छे हैं तो नहीं। यह 'नॉबिश' गलतियों को रोक सकता है। C ++ में एक सीखने की अवस्था है जो उन लोगों को मारता है जो इसे बिल्कुल नहीं बनाते हैं।
एलेक टील

जवाबों:


309

autoमौन निहित रूपांतरणों से बचकर प्रदर्शन में सहायता कर सकता है । एक उदाहरण जो मुझे सम्मोहक लगा वह निम्नलिखित है।

std::map<Key, Val> m;
// ...

for (std::pair<Key, Val> const& item : m) {
    // do stuff
}

बग देखें? यहाँ हम सोच रहे हैं कि हम अपने संदर्भ को स्पष्ट करने के लिए काग्रेस के संदर्भ में मानचित्र में हर एक वस्तु को ले रहे हैं और नई रेंज-फॉर एक्सप्रेशन का उपयोग कर रहे हैं, लेकिन वास्तव में हम हर तत्व की नकल कर रहे हैं । इसका कारण std::map<Key, Val>::value_typeहै std::pair<const Key, Val>, नहीं std::pair<Key, Val>। इस प्रकार, जब हमारे पास (संक्षेप में):

std::pair<Key, Val> const& item = *iter;

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

int const& i = 2.0; // perfectly OK

जैसे रूपांतरण इसी कारण से आप एक में बदल सकते हैं के लिए एक की अनुमति दी अंतर्निहित रूपांतरण है const Keyएक करने के लिए Keyहै, लेकिन हम आदेश है कि के लिए अनुमति देने के लिए नए प्रकार की एक अस्थायी निर्माण करने के लिए है। इस प्रकार, प्रभावी रूप से हमारा लूप करता है:

std::pair<Key, Val> __tmp = *iter;       // construct a temporary of the correct type
std::pair<Key, Val> const& item = __tmp; // then, take a reference to it

(बेशक, वास्तव में कोई __tmpवस्तु नहीं है, यह सिर्फ चित्रण के लिए है, वास्तव में अनाम अस्थायी बस itemअपने जीवनकाल के लिए बाध्य है )।

बस में बदल रहा है:

for (auto const& item : m) {
    // do stuff
}

बस हमें एक टन प्रतियां बचाईं - अब संदर्भित प्रकार आरम्भिक प्रकार से मेल खाता है, इसलिए कोई अस्थायी या रूपांतरण आवश्यक नहीं है, हम सिर्फ एक प्रत्यक्ष संदर्भ कर सकते हैं।


19
@Barry तुम क्यों संकलक खुशी से प्रतियां बनाना होगा बजाय एक इलाज करने की कोशिश के बारे में शिकायत की व्याख्या कर सकते std::pair<const Key, Val> const &एक के रूप में std::pair<Key, Val> const &? C ++ 11 में नया, यह सुनिश्चित नहीं है कि इसमें रेंज-फॉर और autoप्ले कैसा है ।
अगोप

@Barry स्पष्टीकरण के लिए धन्यवाद। वह टुकड़ा जो मुझे याद आ रहा था - किसी कारण से, मुझे लगा कि आपके पास एक अस्थायी के लिए एक निरंतर संदर्भ नहीं हो सकता है। लेकिन निश्चित रूप से आप कर सकते हैं - यह अपने दायरे के अंत में मौजूद नहीं रहेगा।
20

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

38
मुझे अभी भी नहीं लगता कि यह सबूत है कि " autoप्रदर्शन में सुधार होता है"। यह सिर्फ एक उदाहरण है कि " autoप्रोग्रामर गलतियों को रोकने में मदद करता है जो प्रदर्शन को नष्ट करते हैं"। मैं प्रस्तुत करता हूं कि दोनों के बीच एक सूक्ष्म अभी तक महत्वपूर्ण अंतर है। फिर भी, +1।
ऑर्बिट में

70

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

एक सामान्य उदाहरण (ab) से आता है std::function:

std::function<bool(T, T)> cmp1 = std::bind(f, _2, 10, _1);  // bad
auto cmp2 = std::bind(f, _2, 10, _1);                       // good
auto cmp3 = [](T a, T b){ return f(b, 10, a); };            // also good

std::stable_partition(begin(x), end(x), cmp?);

साथ cmp2और cmp3पूरे एल्गोरिथ्म, तुलना कॉल इनलाइन सकता है जबकि अगर आप एक निर्माण std::functionवस्तु, न केवल कॉल inlined नहीं किया जा सकता है, लेकिन आप भी समारोह आवरण के प्रकार-मिट इंटीरियर में बहुरूपी देखने के माध्यम से जाना है।

इस विषय पर एक और प्रकार है कि आप कह सकते हैं:

auto && f = MakeAThing();

यह हमेशा एक संदर्भ है, फ़ंक्शन कॉल अभिव्यक्ति के मूल्य के लिए बाध्य है, और कभी भी कोई अतिरिक्त ऑब्जेक्ट नहीं बनाता है। यदि आपको दिए गए मान के प्रकार का पता नहीं है, तो आपको कुछ के माध्यम से एक नई वस्तु (शायद एक अस्थायी के रूप में) का निर्माण करने के लिए मजबूर किया जा सकता है T && f = MakeAThing()। (इसके अलावा, auto &&यहां तक ​​कि तब भी काम करता है जब रिटर्न प्रकार चल नहीं है और रिटर्न वैल्यू एक प्रचलन है।)


तो यह "प्रकार के क्षरण से बचने" का उपयोग करने का कारण है auto। आपका दूसरा संस्करण "आकस्मिक प्रतियों से बचें" है, लेकिन अलंकरण की आवश्यकता है; autoआप केवल टाइप टाइप करने की गति क्यों देते हैं? (मुझे लगता है कि इसका उत्तर है "आप गलत टाइप करते हैं, और यह चुपचाप धर्मान्तरित हो जाता है") जो इसे बैरी के उत्तर का एक कम अच्छी तरह से समझाया गया उदाहरण बनाता है, नहीं? यानी, दो बुनियादी मामले हैं: ऑटो प्रकार से बचने के लिए ऑटो, और ऑटो से चुप प्रकार की त्रुटियों से बचने के लिए जो गलती से परिवर्तित हो जाते हैं, दोनों की रन टाइम लागत होती है।
Yakk - एडम Nevraumont

2
"न केवल कॉल को इनलाइन नहीं किया जा सकता है" - ऐसा क्यों है? क्या आपका मतलब है कि सिद्धांत कुछ रोकता में कॉल devirtualized किया जा रहा डेटा अगर के प्रासंगिक विशेषज्ञताओं प्रवाह के बाद विश्लेषण करें std::bind, std::functionऔर std::stable_partitionसब inlined किया गया है? या बस उस व्यवहार में कोई C ++ कंपाइलर आक्रामक रूप से इनलाइन को गड़बड़ाने के लिए पर्याप्त नहीं होगा?
स्टीव जेसोप

@SteveJessop: ज्यादातर उत्तरार्द्ध - जब आप std::functionकंस्ट्रक्टर से गुजरते हैं, तो वास्तविक कॉल के माध्यम से देखने के लिए यह बहुत जटिल होगा, विशेष रूप से छोटे-फ़ंक्शन अनुकूलन के साथ (इसलिए आप वास्तव में विचलन नहीं चाहते हैं)। बेशक सिद्धांत में सब कुछ है जैसे कि ...
केरेक एसबी

41

दो श्रेणियां हैं।

autoप्रकार के क्षरण से बच सकते हैं। असंदिग्ध प्रकार (जैसे लंबोदर) हैं, और लगभग अवर्णनीय प्रकार ( std::bindजैसे चीजों के साथ या अन्य अभिव्यक्ति-टेम्पलेट का परिणाम )।

इसके बिना auto, आप अंत में डेटा को कुछ इस तरह से मिटा देते हैं std::function। टाइप इरेज़र की लागत होती है।

std::function<void()> task1 = []{std::cout << "hello";};
auto task2 = []{std::cout << " world\n";};

task1ओवरहैचर ओवरहेड है - एक संभव ढेर आवंटन, इसे स्थापित करने में कठिनाई, और वर्चुअल फ़ंक्शन टेबल इन्वोकेशन ओवरहेड। task2कोई नहीं है लैम्ब्डा को बिना क्षरण के स्टोर करने के लिए ऑटो या अन्य प्रकार के कटौती की आवश्यकता होती है ; अन्य प्रकार इतने जटिल हो सकते हैं कि उन्हें केवल अभ्यास में इसकी आवश्यकता होती है।

दूसरा, आप गलत टाइप कर सकते हैं। कुछ मामलों में, गलत प्रकार पूरी तरह से काम करेगा, लेकिन एक प्रतिलिपि का कारण होगा।

Foo const& f = expression();

expression()रिटर्न Bar const&या Barया Bar&, जहां Fooसे निर्माण किया जा सकता है संकलन करेगा Bar। एक अस्थायी Fooबनाया जाएगा, फिर बाध्य किया जाएगा f, और उसके जीवनकाल को तब तक बढ़ाया जाएगा जब तक कि fवह दूर न हो जाए।

प्रोग्रामर का मतलब हो सकता है Bar const& fऔर वहाँ एक प्रति बनाने का इरादा नहीं है, लेकिन एक प्रति परवाह किए बिना बनाई गई है।

सबसे आम उदाहरण का प्रकार है *std::map<A,B>::const_iterator, जो std::pair<A const, B> const&नहीं है std::pair<A,B> const&, लेकिन त्रुटि त्रुटियों की एक श्रेणी है जो चुपचाप प्रदर्शन की लागत है। आप एक std::pair<A, B>से निर्माण कर सकते हैं std::pair<const A, B>। (एक मानचित्र पर कुंजी कास्ट है, क्योंकि इसे संपादित करना एक बुरा विचार है)

@Barry और @KerrekSB दोनों ने पहले अपने उत्तर में इन दोनों सिद्धांतों को चित्रित किया। यह केवल एक उत्तर में दो मुद्दों को उजागर करने का एक प्रयास है, जिसका उद्देश्य उदाहरण-केंद्रित होने के बजाय समस्या का उद्देश्य है।


9

मौजूदा तीन उत्तर ऐसे उदाहरण देते हैं जहां उपयोग autoकरने से "अनपेक्षित रूप से कम करने की संभावना कम हो जाती है" यह प्रभावी रूप से "प्रदर्शन में सुधार" करता है।

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

const auto    resAuto    = Ha + Vector3(0.,0.,j * 2.567);
const Vector3 resVector3 = Ha + Vector3(0.,0.,j * 2.567);

std::cout << "resAuto = " << resAuto <<std::endl;
std::cout << "resVector3 = " << resVector3 <<std::endl;

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

हालांकि प्रदर्शन यहाँ बहुत प्रभावित नहीं हुआ है, autoअनजाने निराशा से बचने के लिए उपयोग को समय से पहले अनुकूलन के रूप में वर्गीकृत किया जा सकता है, या कम से कम गलत;)।


1
विपरीत सवाल जोड़ा गया: stackoverflow.com/questions/38415831/…
लियोन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.