क्या C ++ में i ++ और ++ i के बीच प्रदर्शन अंतर है?


352

हमारे पास सवाल है कि क्या Ci++ और ++i C के बीच कोई अंतर है ?

C ++ का उत्तर क्या है?


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

104
क्या C ++ और ++ C का उपयोग करने के बीच कोई प्रदर्शन अंतर है?
new123456

2
अनुच्छेद: क्या यह उपसर्ग इंक्रीमेंट ऑपरेटर का उपयोग करना उचित है + पुनरावृत्तियों के बजाय यह उपसर्ग ऑपरेटर के बजाय ++ है? - viva64.com/en/b/0093

जवाबों:


426

[कार्यकारी सारांश: ++iयदि आपके पास उपयोग करने का कोई विशिष्ट कारण नहीं है तो उपयोग करें i++।]

C ++ के लिए, उत्तर थोड़ा अधिक जटिल है।

यदि iएक सरल प्रकार (सी ++ वर्ग का उदाहरण नहीं है), तो सी के लिए दिया गया उत्तर ("कोई प्रदर्शन अंतर नहीं है") रखता है, क्योंकि कंपाइलर कोड उत्पन्न कर रहा है।

हालांकि, अगर i, एक सी ++ वर्ग का एक उदाहरण है तो i++और ++iसे एक के लिए कॉल कर रहे हैं operator++काम करता है। यहाँ इन कार्यों की एक मानक जोड़ी है:

Foo& Foo::operator++()   // called for ++i
{
    this->data += 1;
    return *this;
}

Foo Foo::operator++(int ignored_dummy_value)   // called for i++
{
    Foo tmp(*this);   // variable "tmp" cannot be optimized away by the compiler
    ++(*this);
    return tmp;
}

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


3
संकलक से बचने के लिए संकलक क्या दूसरी कॉपी है, कॉल करने वाले को एनआरवीओ के माध्यम से, एक अन्य टिप्पणी के अनुसार, टीएमपी को वापस करने की आवश्यकता है।
ब्लिसॉर्बलेड

7
यदि संचालक ++ इनलाइन है तो कंपाइलर इससे पूरी तरह बच नहीं सकता है?
एडुआर्ड - गेब्रियल मुंटेनू

16
हां अगर ऑपरेटर ++ इनबिल्ट है और tmp का इस्तेमाल कभी नहीं किया जाता है तो इसे हटाया नहीं जा सकता है जब तक कि tmp ऑब्जेक्ट के कंस्ट्रक्टर या डिस्ट्रक्टर के साइड इफेक्ट्स न हों।
ज़ैन लिंक्स

5
@kriss: C और C ++ के बीच का अंतर यह है कि C में आपको गारंटी है कि ऑपरेटर इनलाइन होगा, और उस समय एक सभ्य अनुकूलक अंतर को दूर करने में सक्षम होगा; C ++ के बजाय आप इनलाइनिंग नहीं मान सकते - हमेशा नहीं।
ब्लिसोरब्लेड

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

64

हाँ। वहाँ है।

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

ऑपरेटर ++ (int) फ़ंक्शन को एक प्रतिलिपि बनाना होगा। ऐसा इसलिए है क्योंकि पोस्टफ़िक्स ++ से यह उम्मीद की जाती है कि वह जो भी मूल्य रखता है, उसकी तुलना में एक अलग मान लौटाए: उसे एक अस्थायी चर में अपना मूल्य रखना चाहिए, अपना मूल्य बढ़ाना चाहिए और अस्थायी वापस लौटना चाहिए। ऑपरेटर ++ (), उपसर्ग ++ के मामले में, प्रतिलिपि बनाने की कोई आवश्यकता नहीं है: ऑब्जेक्ट खुद को बढ़ा सकता है और फिर बस खुद को वापस कर सकता है।

यहाँ बिंदु का एक चित्रण है:

struct C
{
    C& operator++();      // prefix
    C  operator++(int);   // postfix

private:

    int i_;
};

C& C::operator++()
{
    ++i_;
    return *this;   // self, no copy created
}

C C::operator++(int ignored_dummy_value)
{
    C t(*this);
    ++(*this);
    return t;   // return a copy
}

हर बार जब आप ऑपरेटर ++ (इंट) कहते हैं, तो आपको एक कॉपी बनानी होगी, और कंपाइलर इसके बारे में कुछ नहीं कर सकता है। जब विकल्प दिया जाता है, तो ऑपरेटर ++ () का उपयोग करें; इस तरह आप एक कॉपी नहीं बचाते हैं। यह कई वेतन वृद्धि (बड़े लूप?) और / या बड़ी वस्तुओं के मामले में महत्वपूर्ण हो सकता है।


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

पोस्टफ़िक्स कोड में, यह काम कैसे करता C t(*this); ++(*this); return t;है दूसरी पंक्ति में, आप इस पॉइंटर को सही से बढ़ा रहे हैं, इसलिए tयदि आप इसे बढ़ा रहे हैं तो कैसे अपडेट किया जाए। पहले से ही कॉपी किए गए इस के मूल्य नहीं थे t?
rasen58

The operator++(int) function must create a copy.नहीं ऐसा नहीं है। से अधिक प्रतियां नहींoperator++()
सेवेरिन पैपडेक्स

47

इस मामले के लिए एक बेंचमार्क है जब वेतन वृद्धि ऑपरेटर विभिन्न अनुवाद इकाइयों में हैं। जी ++ 4.5 के साथ कंपाइलर।

अभी के लिए शैली के मुद्दों पर ध्यान न दें

// a.cc
#include <ctime>
#include <array>
class Something {
public:
    Something& operator++();
    Something operator++(int);
private:
    std::array<int,PACKET_SIZE> data;
};

int main () {
    Something s;

    for (int i=0; i<1024*1024*30; ++i) ++s; // warm up
    std::clock_t a = clock();
    for (int i=0; i<1024*1024*30; ++i) ++s;
    a = clock() - a;

    for (int i=0; i<1024*1024*30; ++i) s++; // warm up
    std::clock_t b = clock();
    for (int i=0; i<1024*1024*30; ++i) s++;
    b = clock() - b;

    std::cout << "a=" << (a/double(CLOCKS_PER_SEC))
              << ", b=" << (b/double(CLOCKS_PER_SEC)) << '\n';
    return 0;
}

ओ (एन) वेतन वृद्धि

परीक्षा

// b.cc
#include <array>
class Something {
public:
    Something& operator++();
    Something operator++(int);
private:
    std::array<int,PACKET_SIZE> data;
};


Something& Something::operator++()
{
    for (auto it=data.begin(), end=data.end(); it!=end; ++it)
        ++*it;
    return *this;
}

Something Something::operator++(int)
{
    Something ret = *this;
    ++*this;
    return ret;
}

परिणाम

वर्चुअल मशीन पर g ++ 4.5 के साथ परिणाम (समय सेकंड में हैं):

Flags (--std=c++0x)       ++i   i++
-DPACKET_SIZE=50 -O1      1.70  2.39
-DPACKET_SIZE=50 -O3      0.59  1.00
-DPACKET_SIZE=500 -O1    10.51 13.28
-DPACKET_SIZE=500 -O3     4.28  6.82

ओ (१) वेतन वृद्धि

परीक्षा

आइए अब हम निम्नलिखित फ़ाइल लेते हैं:

// c.cc
#include <array>
class Something {
public:
    Something& operator++();
    Something operator++(int);
private:
    std::array<int,PACKET_SIZE> data;
};


Something& Something::operator++()
{
    return *this;
}

Something Something::operator++(int)
{
    Something ret = *this;
    ++*this;
    return ret;
}

यह वेतन वृद्धि में कुछ नहीं करता है। यह मामला अनुकरण करता है जब वृद्धि में निरंतर जटिलता होती है।

परिणाम

अब परिणाम बहुत भिन्न होते हैं:

Flags (--std=c++0x)       ++i   i++
-DPACKET_SIZE=50 -O1      0.05   0.74
-DPACKET_SIZE=50 -O3      0.08   0.97
-DPACKET_SIZE=500 -O1     0.05   2.79
-DPACKET_SIZE=500 -O3     0.08   2.18
-DPACKET_SIZE=5000 -O3    0.07  21.90

निष्कर्ष

अभिनय की दृष्टि से

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

सिमेंटिक वार

  • i++कहता है increment i, I am interested in the previous value, though
  • ++iincrement i, I am interested in the current valueया कहता है increment i, no interest in the previous value। फिर, आपको इसकी आदत हो जाएगी, भले ही आप अभी नहीं हैं।

नुथ।

सभी बुराईयो की जड़ समयपूर्व इष्टतमीकरण है। जैसा कि समयपूर्व निराशा है।


1
दिलचस्प परीक्षण। अब, लगभग ढाई साल बाद, gcc 4.9 और Clang 3.4 एक समान प्रवृत्ति दिखाते हैं। दोनों के साथ क्लैंग थोड़ा तेज है, लेकिन प्री और पोस्टफिक्स के बीच असमानता gcc से भी बदतर है।
मोजे

मैं वास्तव में जो देखना चाहता हूं वह एक वास्तविक दुनिया का उदाहरण है जहां ++ i / i ++ से फर्क पड़ता है। उदाहरण के लिए, क्या यह एसटीडी पुनरावृत्तियों में से किसी पर फर्क पड़ता है?
जैकब शॉ जेन्सेन

@JakobSchouJensen: ये वास्तविक दुनिया के उदाहरण बनने के लिए बहुत इच्छुक थे। जटिल वृक्ष संरचनाओं (जैसे kd- पेड़, क्वाड-ट्री) या अभिव्यक्ति टेम्पलेट्स में उपयोग किए जाने वाले बड़े कंटेनरों (SIMD हार्डवेयर पर डेटा थ्रूपुट को अधिकतम करने के लिए) के साथ एक बड़े अनुप्रयोग पर विचार करें। अगर इससे कोई फर्क पड़ता है, तो मुझे वास्तव में यकीन नहीं है कि क्यों किसी को विशिष्ट मामलों के लिए पोस्ट-इन्क्रीमेंट के लिए कमबैक करना होगा अगर इसे शब्दार्थ-वार की आवश्यकता नहीं है।
सेबेस्टियन मच

@phresnel: मुझे नहीं लगता कि ऑपरेटर ++ आपके रोजमर्रा के अभिव्यक्ति टेम्पलेट में है - क्या आपके पास इसका वास्तविक उदाहरण है? ऑपरेटर ++ का विशिष्ट उपयोग पूर्णांक और पुनरावृत्तियों पर है। Thats मुझे लगता है कि यह जानना दिलचस्प होगा कि क्या कोई अंतर है (पूर्णांकों के अंतर पर कोई अंतर नहीं है - लेकिन पुनरावृत्तियों)।
जैकब शॉ जेन्सेन

@JakobSchouJensen: कोई वास्तविक व्यावसायिक उदाहरण नहीं है, लेकिन कुछ संख्या में जहां आप सामान की गिनती करते हैं, वहां ऐप्लीकेशन। लेखक पुनरावृत्तियों, एक किरण अनुरेखक पर विचार करें जो मुहावरेदार सी ++ शैली में लिखा गया है, और आपके पास गहराई-पहले for (it=nearest(ray.origin); it!=end(); ++it) { if (auto i = intersect(ray, *it)) return i; }त्रैमासिक के लिए एक पुनरावृत्ति है , जैसे कि , वास्तविक पेड़ संरचनाकार (बीएसपी, केडी, क्वाडट्री, ऑक्ट्री ग्रिड, आदि) पर कभी भी ध्यान न दें। इस तरह के एक इटरेटर कुछ राज्य, जैसे बनाए रखने के लिए की आवश्यकता होगी parent node, child node, indexऔर इस तरह सामान। सब सब में, मेरा रुख है, भले ही केवल कुछ उदाहरण मौजूद हैं, ...
सेबस्टियन मच

20

यह कहना पूरी तरह से सही नहीं है कि संकलक पोस्टफ़िक्स मामले में अस्थायी परिवर्तनशील प्रतिलिपि को दूर नहीं कर सकता है। वीसी के साथ एक त्वरित परीक्षण से पता चलता है कि यह, कम से कम, कुछ मामलों में ऐसा कर सकता है।

निम्न उदाहरण में, उत्पन्न कोड उपसर्ग और उपसर्ग के लिए समान है, उदाहरण के लिए:

#include <stdio.h>

class Foo
{
public:

    Foo() { myData=0; }
    Foo(const Foo &rhs) { myData=rhs.myData; }

    const Foo& operator++()
    {
        this->myData++;
        return *this;
    }

    const Foo operator++(int)
    {
        Foo tmp(*this);
        this->myData++;
        return tmp;
    }

    int GetData() { return myData; }

private:

    int myData;
};

int main(int argc, char* argv[])
{
    Foo testFoo;

    int count;
    printf("Enter loop count: ");
    scanf("%d", &count);

    for(int i=0; i<count; i++)
    {
        testFoo++;
    }

    printf("Value: %d\n", testFoo.GetData());
}

चाहे आप ++ टेस्टफू या टेस्टफू ++ करते हैं, फिर भी आपको वही परिणामी कोड मिलेगा। वास्तव में, उपयोगकर्ता से गिनती को पढ़े बिना, ऑप्टिमाइज़र ने पूरी चीज़ को स्थिर कर दिया। तो यह:

for(int i=0; i<10; i++)
{
    testFoo++;
}

printf("Value: %d\n", testFoo.GetData());

निम्नलिखित में परिणाम:

00401000  push        0Ah  
00401002  push        offset string "Value: %d\n" (402104h) 
00401007  call        dword ptr [__imp__printf (4020A0h)] 

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


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

14

गूगल सी ++ स्टाइल गाइड का कहना है:

पूर्वनिर्धारण और पूर्वनिर्धारण

पुनरावृत्तियों और वेतन वृद्धि संचालकों के उपसर्ग प्रपत्र (++ i) का उपयोग करें पुनरावृत्तियों और अन्य टेम्पलेट ऑब्जेक्ट्स के साथ।

परिभाषा: जब एक चर बढ़ाया जाता है (++ i या i ++) या decremented (--i या i--) और अभिव्यक्ति के मूल्य का उपयोग नहीं किया जाता है, तो किसी को यह तय करना होगा कि प्रीइंक्रिमेंट (decrement) या postincrement (डिक्रीमेंट) करना है या नहीं।

पेशेवरों: जब रिटर्न वैल्यू को नजरअंदाज किया जाता है, तो "प्री" फॉर्म (++ i) "पोस्ट" फॉर्म (i ++) की तुलना में कभी कम कुशल नहीं होता है, और अक्सर अधिक कुशल होता है। ऐसा इसलिए है क्योंकि पोस्ट-इन्क्रीमेंट (या डिक्रीमेंट) के लिए i की एक प्रति की आवश्यकता होती है, जो कि अभिव्यक्ति का मूल्य है। यदि मैं एक पुनरावृत्त या अन्य गैर-स्केलर प्रकार है, तो नकल करना मैं महंगा हो सकता है। चूंकि दो प्रकार की वेतन वृद्धि समान व्यवहार करती है जब मूल्य की अनदेखी की जाती है, तो हमेशा पूर्व वेतन वृद्धि क्यों नहीं होती है?

विपक्ष: परंपरा विकसित हुई, सी में, पोस्ट-इंक्रीमेंट का उपयोग करते समय जब अभिव्यक्ति मूल्य का उपयोग नहीं किया जाता है, विशेष रूप से छोरों के लिए। कुछ लोग पोस्ट-इन्क्रीमेंट को पढ़ना आसान समझते हैं, क्योंकि "सब्जेक्ट" (i) इंग्लिश की तरह "वर्ब" (++) से पहले है।

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


1
"निर्णय: साधारण स्केलर (गैर-वस्तु) मूल्यों के लिए एक रूप को पसंद करने का कोई कारण नहीं है और हम या तो अनुमति देते हैं। पुनरावृत्तियों और अन्य टेम्पलेट प्रकारों के लिए, पूर्व-वेतन वृद्धि का उपयोग करें।"
नोसरेडना

2
एह, ..., और वह क्या चीज है?
सेबस्टियन मच

उत्तर में उल्लिखित लिंक वर्तमान में टूटा हुआ है
karol

4

मैं हाल ही में कोड टॉक पर एंड्रयू कोएनिग द्वारा एक उत्कृष्ट पोस्ट को इंगित करना चाहता हूं।

http://dobbscodetalk.com/index.php?option=com_myblog&show=Efficiency-versus-intent.html&Itemid=29

हमारी कंपनी में भी हम स्थिरता और प्रदर्शन के लिए ++ iter के सम्मेलन का उपयोग करते हैं जहां लागू होता है। लेकिन इरादे बनाम प्रदर्शन के बारे में एंड्रयू ने विस्तार से देखा। ऐसे समय होते हैं जब हम ++ iter के बजाय iter ++ का उपयोग करना चाहते हैं।

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


अद्यतित लिंक: drdobbs.com/ Ernakulamecture
Ofek Shilon

4

@Ketan

... इरादे बनाम प्रदर्शन के बारे में अधिक विस्तार से देखा। ऐसे समय होते हैं जब हम ++ iter के बजाय iter ++ का उपयोग करना चाहते हैं।

स्पष्ट रूप से पोस्ट और पूर्व-वेतन वृद्धि के अलग-अलग शब्दार्थ हैं और मुझे यकीन है कि हर कोई इससे सहमत है कि जब परिणाम का उपयोग किया जाता है तो आपको उपयुक्त ऑपरेटर का उपयोग करना चाहिए। मुझे लगता है कि सवाल यह है कि रिजल्ट ( forछोरों की तरह) खारिज होने पर क्या करना चाहिए । इस प्रश्न (IMHO) का उत्तर यह है, क्योंकि प्रदर्शन के विचार सबसे अच्छे रूप से नगण्य हैं, इसलिए आपको वही करना चाहिए जो अधिक स्वाभाविक है। खुद के ++iलिए अधिक स्वाभाविक है, लेकिन मेरा अनुभव बताता है कि मैं अल्पमत में हूं और अधिकांश केi++ लिए कम धातु के उपरि का उपयोग करूंगा आपके कोड को पढ़ने वाले लोगों के ।

आखिरकार यही कारण है कि भाषा को " ++C" नहीं कहा जाता है । [*]

[*] ++Cअधिक तार्किक नाम होने के बारे में अनिवार्य चर्चा डालें ।


4
@Motti: (मजाक करते हुए) C ++ नाम तार्किक है यदि आप Bjarne Stroustrup C ++ को याद करते हैं, तो शुरू में इसे C- प्रोग्राम बनाने वाले प्री-कंपाइलर के रूप में कोडित किया गया था। इसलिए C ++ ने एक पुराना C मान लौटाया। या यह बढ़ाना हो सकता है कि C ++ शुरू से ही कुछ वैचारिक रूप से दोषपूर्ण है।
kriss

4
  1. ++ i - तेजी से वापसी मूल्य का उपयोग नहीं कर रहा है
  2. i ++ - रिटर्न वैल्यू का उपयोग करके तेजी से

जब वापसी मूल्य का उपयोग नहीं करते हैं तो संकलक को गारंटी दी जाती है कि वह ++ i के मामले में एक अस्थायी का उपयोग न करें । तेज़ होने की गारंटी नहीं है, लेकिन धीमी नहीं होने की गारंटी है।

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


3

मार्क: बस यह बताना चाहता था कि ऑपरेटर ++ के अच्छे उम्मीदवार हैं, और यदि कंपाइलर ऐसा करने के लिए चुनाव करता है, तो ज्यादातर मामलों में निरर्थक कॉपी को समाप्त कर दिया जाएगा। (उदाहरण POD प्रकार, जो पुनरावृत्तियां आमतौर पर होती हैं।)

कहा कि, यह अभी भी बेहतर है कि ज्यादातर मामलों में ++ iter का उपयोग करें। :-)


3

जब आप ऑपरेटरों को मूल्य-वापसी कार्यों के रूप में सोचते हैं और उन्हें कैसे लागू किया जाता है, तो इसके बीच का अंतर ++iऔर i++अधिक स्पष्ट होगा। यह समझना आसान बनाने के लिए कि क्या हो रहा है, निम्न कोड उदाहरण का उपयोग intकरेंगे जैसे कि यह एक था struct

++iचर बढ़ाता है, फिर परिणाम देता है। यह कई मामलों में कोड की केवल एक पंक्ति की आवश्यकता के साथ जगह में और न्यूनतम सीपीयू समय के साथ किया जा सकता है:

int& int::operator++() { 
     return *this += 1;
}

लेकिन उसी के बारे में नहीं कहा जा सकता है i++

वेतन वृद्धि के बाद, i++अक्सर वेतन वृद्धि से पहले मूल मूल्य को लौटाने के रूप में देखा जाता है । हालाँकि, कोई फ़ंक्शन केवल परिणाम समाप्त होने पर वापस आ सकता है । परिणामस्वरूप, मूल मान वाले चर की एक प्रति बनाना आवश्यक हो जाता है, चर को बढ़ाता है, फिर मूल मान को पकड़े हुए प्रति वापस लौटाता है:

int int::operator++(int& _Val) {
    int _Original = _Val;
    _Val += 1;
    return _Original;
}

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


1

@ मर्क: मैंने अपने पिछले उत्तर को हटा दिया क्योंकि यह थोड़ा फ्लिप था, और उस अकेले के लिए डाउनवोट के हकदार थे। मुझे वास्तव में लगता है कि यह इस अर्थ में एक अच्छा सवाल है कि यह पूछता है कि बहुत सारे लोगों के दिमाग में क्या है।

सामान्य उत्तर यह है कि ++ मैं i ++ से तेज है, और इसमें कोई संदेह नहीं है, लेकिन बड़ा सवाल यह है कि "आपको कब ध्यान देना चाहिए?"

यदि पुनरावृत्तियों को बढ़ाने में सीपीयू समय का अंश 10% से कम है, तो आप परवाह नहीं कर सकते हैं।

यदि पुनरावृत्तियों को बढ़ाने में CPU समय का अंश 10% से अधिक है, तो आप देख सकते हैं कि कौन से कथन उस छेड़छाड़ कर रहे हैं। देखें कि क्या आप पुनरावृत्तियों का उपयोग करने के बजाय पूर्णांक बढ़ा सकते हैं। संभावना है कि आप कर सकते हैं, और जबकि यह कुछ मायने में कम वांछनीय हो सकता है, संभावना बहुत अच्छी है आप अनिवार्य रूप से उन पुनरावृत्तियों में खर्च किए गए सभी समय को बचाएंगे।

मैंने एक उदाहरण देखा है जहां 90% से अधिक समय में इट्रेटर-इंक्रीमेंट अच्छी तरह से उपभोग कर रहा था। उस मामले में, पूर्णांक-इंक्रीमेंट पर जाने से अनिवार्य रूप से उस राशि से निष्पादन समय कम हो जाता है। (यानी 10x स्पीडअप से बेहतर)


1

@wilhelmtell

संकलक अस्थायी को खत्म कर सकता है। दूसरे धागे से शब्दभेदी:

C ++ कंपाइलर को स्टैक आधारित टेम्परेरीज़ को खत्म करने की अनुमति दी जाती है, भले ही ऐसा करने से प्रोग्राम का व्यवहार बदल जाए। VCDN के लिए MSDN लिंक:

http://msdn.microsoft.com/en-us/library/ms364057(VS.80).aspx


1
यह प्रासंगिक नहीं है। NRVO कॉलर में वापस "CC :: ऑपरेटर ++ (int)" में t कॉपी करने की आवश्यकता को टालता है, लेकिन i ++ कॉल करने वाले के स्टैक पर पुराने मूल्य को कॉपी करेगा। NRVO के बिना, i ++ 2 प्रतियां बनाता है, एक को टी और एक को कॉलर को वापस।
Blaisorblade

0

एक कारण यह है कि आप ++ का उपयोग करना चाहते हैं यहां तक ​​कि अंतर्निहित प्रकारों पर जहां कोई प्रदर्शन लाभ नहीं है, अपने लिए एक अच्छी आदत बनाने के लिए है।


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

@MikeDunlavey ठीक है, तो आप किस पक्ष का उपयोग करते हैं जब यह कोई फर्क नहीं पड़ता? xD यह या तो एक है या दूसरे इसे नहीं है! पोस्ट ++ (यदि आप इसे सामान्य अर्थ के साथ उपयोग कर रहे हैं। इसे अपडेट करें, पुराना वापस लौटाएं) ++ प्री से पूरी तरह से हीन है (इसे अपडेट करें, वापस करें) ऐसा कोई कारण नहीं है जिससे आप कम प्रदर्शन करना चाहते हैं। उस स्थिति में जहां आप इसे अपडेट करना चाहते हैं, प्रोग्रामर उस समय के बाद भी ++ नहीं करेगा। जब हमारे पास पहले से कोई समय बर्बाद न हो तो नकल करना। इसका उपयोग करने के बाद हम इसे अपडेट करते हैं। तो आप चाहते थे कि सामान्य ज्ञान के संकलक है।
पोखर

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

@ मायिक डनलैवे ने अपने अहंकार को संतुष्ट करने के लिए बकवास किया। आप कुछ सभी बुद्धिमान भिक्षुओं की तरह आवाज करने की कोशिश कर रहे हैं, फिर भी आप कुछ नहीं कह रहे हैं। परिमाण में शामिल ... यदि केवल 1% से अधिक समय आपको देखभाल करना चाहिए ... xD पूर्ण ड्रिबल। यदि यह अक्षम है, तो इसके बारे में जानने और ठीक करने के लायक है। हम यहाँ उस सटीक कारण के लिए विचार कर रहे हैं! हम इस बारे में चिंतित नहीं हैं कि हम इस ज्ञान से कितना लाभ उठा सकते हैं। और जब मैंने कहा कि आप कम प्रदर्शन नहीं चाहते हैं, तो आगे बढ़ें, एक लानत परिदृश्य की व्याख्या करें। MR WISE!
पोद्दले

0

दोनों ही उतने ही तेज़ हैं;) यदि आप चाहते हैं कि यह प्रोसेसर के लिए समान गणना है, तो यह सिर्फ उसी क्रम में होता है जिसमें यह भिन्न होता है।

उदाहरण के लिए, निम्न कोड:

#include <stdio.h>

int main()
{
    int a = 0;
    a++;
    int b = 0;
    ++b;
    return 0;
}

निम्नलिखित विधानसभा का निर्माण करें:

 0x0000000100000f24 <main+0>: push   %rbp
 0x0000000100000f25 <main+1>: mov    %rsp,%rbp
 0x0000000100000f28 <main+4>: movl   $0x0,-0x4(%rbp)
 0x0000000100000f2f <main+11>:    incl   -0x4(%rbp)
 0x0000000100000f32 <main+14>:    movl   $0x0,-0x8(%rbp)
 0x0000000100000f39 <main+21>:    incl   -0x8(%rbp)
 0x0000000100000f3c <main+24>:    mov    $0x0,%eax
 0x0000000100000f41 <main+29>:    leaveq 
 0x0000000100000f42 <main+30>:    retq

आप देखते हैं कि ++ और b ++ के लिए यह एक इन्क्लोमेनिक है, इसलिए यह एक ही ऑपरेशन है;)


यह C है, जबकि OP ने C ++ से पूछा। C में यह समान है। C ++ में तेज + ++ i है; अपनी वस्तु के कारण। हालाँकि कुछ कंपाइलर पोस्ट-इन्क्रीमेंट ऑपरेटर को ऑप्टिमाइज़ कर सकते हैं।
विगलेर जटाग

0

इच्छित प्रश्न तब था जब परिणाम अप्रयुक्त है (सी के लिए प्रश्न से स्पष्ट है)। क्या कोई इसे ठीक कर सकता है क्योंकि सवाल "सामुदायिक विकि" है?

समय से पहले अनुकूलन के बारे में, नुथ अक्सर उद्धृत किया जाता है। ये सही है। लेकिन डोनाल्ड नथ कभी भी उस भयानक कोड का बचाव नहीं करेंगे जो आप इन दिनों में देख सकते हैं। कभी Java Integers (int नहीं) के बीच a = b + c देखा है? यह 3 बॉक्सिंग / अनबॉक्सिंग रूपांतरणों की मात्रा है। ऐसे सामान से बचना जरूरी है। और बेकार में I + + के बजाय i + लिखना एक ही गलती है। संपादित करें: जैसा कि phresnel अच्छी तरह से एक टिप्पणी में रखता है, इसे "समयपूर्व अनुकूलन बुराई के रूप में, समय से पहले निराशाकरण" के रूप में अभिव्यक्त किया जा सकता है।

इस तथ्य पर भी कि लोग I ++ के अधिक अभ्यस्त हैं, एक दुर्भाग्यपूर्ण सी विरासत है, जो K & R द्वारा वैचारिक गलती के कारण होती है (यदि आप आशय तर्क का पालन करते हैं, तो यह एक तार्किक निष्कर्ष है, और K & R का बचाव करना क्योंकि वे K & R व्यर्थ हैं, वे हैं; महान, लेकिन वे भाषा डिजाइनर के रूप में महान नहीं हैं, सी डिजाइन में अनगिनत गलतियां मौजूद हैं, जो () (से) से लेकर strccpy (), एपीआई (इसमें 1 दिन से strlcpy () एपीआई होना चाहिए) तक थी। )।

Btw, मैं उन लोगों में से एक हूं, जिन्होंने C ++ का उपयोग करने के लिए पर्याप्त उपयोग नहीं किया है। फिर भी, मैं इसका उपयोग करता हूं क्योंकि मैं स्वीकार करता हूं कि यह सही है।


मैं देख रहा हूं कि आप पीएचडी पर काम कर रहे हैं। संकलक अनुकूलन और उस तरह की चीजों में रुचि के साथ। यह बहुत अच्छा है, लेकिन यह मत भूलना कि एकेडमिया एक प्रतिध्वनि कक्ष है, और सामान्य ज्ञान अक्सर दरवाजे के बाहर छोड़ दिया जाता है, कम से कम सीएस में आपको इसमें रुचि हो सकती है: stackoverflow.com/questions/1303899/-
माइक डनलेवेट

मैंने कभी भी इससे ++iअधिक कष्टप्रद नहीं पाया i++(वास्तव में, मैंने इसे कूलर पाया), लेकिन आपके बाकी पोस्ट को मेरी पूर्ण स्वीकृति प्राप्त है। शायद एक बिंदु जोड़ें "समय से पहले अनुकूलन बुराई है, जैसा कि समयपूर्व निराशा है"
सेबस्टियन मच

strncpyउस समय उपयोग किए जा रहे फाइलसिस्टम में एक उद्देश्य को पूरा किया; फ़ाइल नाम 8-वर्ण बफर था और इसे शून्य-समाप्त नहीं किया जाना था। आप भाषा के विकास के भविष्य में 40 साल तक नहीं देखने के लिए उन्हें दोष नहीं दे सकते।
एमएम

@MattMcNabb: क्या MS-DOS अनन्य नाम से 8 वर्ण नहीं बनाए गए थे? C का आविष्कार यूनिक्स के साथ किया गया था। वैसे भी, भले ही strncpy में एक बिंदु था, strlcpy की कमी पूरी तरह से उचित नहीं थी: यहां तक ​​कि मूल C में सरणियाँ थीं जिन्हें आपको अतिप्रवाह नहीं करना चाहिए, जिन्हें strlcpy की आवश्यकता थी; कम से कम, वे केवल हमलावरों के कीड़े के शोषण के इरादे से गायब थे। लेकिन कोई यह नहीं कह सकता है कि इस समस्या का पूर्वानुमान करना तुच्छ था, इसलिए यदि मैंने अपनी पोस्ट को फिर से लिखा तो मैं उसी टोन का उपयोग नहीं करूंगा।
Blaisorblade

@ ब्लेज़रब्लड: जैसा कि मुझे याद है, प्रारंभिक UNIX फ़ाइल नाम 14 वर्णों तक सीमित थे। की कमी strlcpy()इस तथ्य से उचित थी कि अभी तक इसका आविष्कार नहीं किया गया था।
कीथ थॉम्पसन

-1

ज्ञान के रत्नों के साथ लोगों को प्रदान करने का समय;) - सी ++ पोस्टफिक्स इन्क्रीमेंट बनाने के लिए सरल ट्रिक है, जो प्रीफ़िक्स इन्क्रीमेंट के समान ही व्यवहार करता है (इसे खुद के लिए आविष्कार किया है, लेकिन इसे अन्य लोगों के कोड में भी देखा है, इसलिए ऐसा नहीं है) अकेला)।

मूल रूप से, चाल वापसी के बाद वेतन वृद्धि को स्थगित करने के लिए सहायक वर्ग का उपयोग करना है, और आरएआईआई बचाव के लिए आता है

#include <iostream>

class Data {
    private: class DataIncrementer {
        private: Data& _dref;

        public: DataIncrementer(Data& d) : _dref(d) {}

        public: ~DataIncrementer() {
            ++_dref;
        }
    };

    private: int _data;

    public: Data() : _data{0} {}

    public: Data(int d) : _data{d} {}

    public: Data(const Data& d) : _data{ d._data } {}

    public: Data& operator=(const Data& d) {
        _data = d._data;
        return *this;
    }

    public: ~Data() {}

    public: Data& operator++() { // prefix
        ++_data;
        return *this;
    }

    public: Data operator++(int) { // postfix
        DataIncrementer t(*this);
        return *this;
    }

    public: operator int() {
        return _data;
    }
};

int
main() {
    Data d(1);

    std::cout <<   d << '\n';
    std::cout << ++d << '\n';
    std::cout <<   d++ << '\n';
    std::cout << d << '\n';

    return 0;
}

इन्वेंटेड कुछ भारी कस्टम पुनरावृत्तियों कोड के लिए है, और यह रन-टाइम में कटौती करता है। उपसर्ग बनाम पोस्टफिक्स की लागत अब एक संदर्भ है, और यदि यह कस्टम ऑपरेटर भारी घूम रहा है, तो उपसर्ग और पोस्टफिक्स मेरे लिए एक ही रन-टाइम पैदा करते हैं।


-5

++iसे अधिक तेज़ है i++क्योंकि यह मान की पुरानी प्रतिलिपि नहीं लौटाता है।

यह भी अधिक सहज है:

x = i++;  // x contains the old value of i
y = ++i;  // y contains the new value of i 

यह C उदाहरण "12" के बजाय "02" को प्रिंट करता है जिसकी आप अपेक्षा कर सकते हैं:

#include <stdio.h>

int main(){
    int a = 0;
    printf("%d", a++);
    printf("%d", ++a);
    return 0;
}

सी ++ के लिए भी :

#include <iostream>
using namespace std;

int main(){
    int a = 0;
    cout << a++;
    cout << ++a;
    return 0;
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.