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




performance c++11 (3)

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

एक विशिष्ट उदाहरण std::function का उपयोग करके (ab) आता है:

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 ऑब्जेक्ट का निर्माण करते हैं, तो न केवल कॉल इनबिल्ट नहीं हो सकती है, बल्कि आपको टाइप-मिट इंटीरियर में पॉलिमॉर्फिक लुकअप से भी गुजरना होगा समारोह आवरण के।

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

auto && f = MakeAThing();

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

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

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

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

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

auto बिना, आप अंत में std::function जैसे डेटा को मिटाने के लिए टाइप 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& not std::pair<A,B> const& , लेकिन त्रुटि त्रुटियों की एक श्रेणी है जो चुपचाप प्रदर्शन की लागत करती है। आप std::pair<const A, B> से एक std::pair<const A, B> । (एक मानचित्र पर कुंजी कास्ट है, क्योंकि इसे संपादित करना एक बुरा विचार है)

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


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

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

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

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

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






auto