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


471

वहाँ के बीच एक प्रदर्शन अंतर है i++और ++iअगर परिणामस्वरूप मूल्य नहीं किया जाता है?


1
संबंधित, सी ++ के लिए एक ही सवाल लेकिन स्पष्ट रूप से stackoverflow.com/questions/24901/… है
FluxLemur

जवाबों:


396

कार्यकारी सारांश: नहीं।

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

हम इस फ़ंक्शन के लिए ++iऔर दोनों के साथ कोड को देखकर इसका प्रदर्शन कर सकते हैं i++

$ cat i++.c
extern void g(int i);
void f()
{
    int i;

    for (i = 0; i < 100; i++)
        g(i);

}

फ़ाइलें एक ही हैं, को छोड़कर ++iऔर i++:

$ diff i++.c ++i.c
6c6
<     for (i = 0; i < 100; i++)
---
>     for (i = 0; i < 100; ++i)

हम उन्हें संकलित करेंगे, और उत्पन्न कोडांतरक भी प्राप्त करेंगे:

$ gcc -c i++.c ++i.c
$ gcc -S i++.c ++i.c

और हम देख सकते हैं कि उत्पन्न वस्तु और कोडांतरक दोनों फाइलें समान हैं।

$ md5 i++.s ++i.s
MD5 (i++.s) = 90f620dda862cd0205cd5db1f2c8c06e
MD5 (++i.s) = 90f620dda862cd0205cd5db1f2c8c06e

$ md5 *.o
MD5 (++i.o) = dd3ef1408d3a9e4287facccec53f7d22
MD5 (i++.o) = dd3ef1408d3a9e4287facccec53f7d22

9
मुझे पता है कि यह प्रश्न सी के बारे में है, लेकिन मुझे यह जानने में दिलचस्पी होगी कि क्या ब्राउज़र जावास्क्रिप्ट के लिए यह अनुकूलन कर सकते हैं।
टीएम।

69
तो "सं" आपके द्वारा परीक्षण किए गए एक संकलक के लिए सही है।
एंड्रियास

173
@ सवाल: अच्छा सवाल। मैं एक संकलक लेखक हुआ करता था, और इस कोड को कई सीपीयू, ऑपरेटिंग सिस्टम और संकलक पर परीक्षण करने का अवसर मिला। एकमात्र कंपाइलर जो मैंने पाया कि i ++ केस को ऑप्टिमाइज़ नहीं किया है (वास्तव में, यह कंपाइलर जो मेरे ध्यान में आया है पेशेवर रूप से) वॉल्ट बिलोफ़्स्की द्वारा सॉफ्टवेयर टूलवर्क्स C80 कंपाइलर था। यह संकलक Intel 8080 CP / M सिस्टम के लिए था। यह कहना सुरक्षित है कि कोई भी कंपाइलर जिसमें इस अनुकूलन को शामिल नहीं किया गया है, वह सामान्य उपयोग के लिए नहीं है।
मार्क हैरिसन

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

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

112

से क्षमता आशय बनाम एंड्रयू कोएनिग द्वारा:

सबसे पहले, यह स्पष्ट है कि कम से कम जहां पूर्णांक चर का संबंध है, ++iसे अधिक कुशल है i++

तथा :

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

इसलिए, यदि परिणामी मूल्य का उपयोग नहीं किया जाता है, तो मैं उपयोग करूंगा ++i। लेकिन इसलिए नहीं कि यह अधिक कुशल है: क्योंकि यह मेरे इरादे को सही ढंग से बताता है।


5
आइए यह न भूलें कि अन्य यूनिरी ऑपरेटर्स भी उपसर्ग हैं। मुझे लगता है कि ++ मैं एक गैर-संचालक का उपयोग करने का "अर्थ" तरीका है, जबकि i ++ एक विशिष्ट आवश्यकता (इसके अलावा मूल्यांकन से पहले) को फिट करने के लिए चारों ओर है।
टीएम।

9
यदि परिणामी मूल्य का उपयोग नहीं किया जाता है, तो शब्दार्थ में कोई अंतर नहीं है : यानी, एक या दूसरे निर्माण को पसंद करने का कोई आधार नहीं है।
Eamon Nerbonne

3
एक पाठक के रूप में, मुझे एक अंतर दिखाई देता है। इसलिए एक लेखक के रूप में, मैं एक दूसरे को चुनकर अपना इरादा दिखाऊंगा। यह सिर्फ मेरी आदत है, मैं अपने प्रोग्रामर दोस्तों और साथियों के साथ कोड के माध्यम से संवाद करने की कोशिश कर रहा हूँ :)
सेबास्टियन रोकासेरा

11
कोएनिग की सलाह के बाद, मैं कोड i++उसी तरह मैं कोड चाहते i += nया i = i + nअर्थात रूप में, लक्ष्य क्रिया वस्तु , साथ लक्ष्य संकार्य की बाईं करने के लिए क्रिया ऑपरेटर। के मामले में i++, वहाँ कोई सही है वस्तु है, लेकिन यह नियम अब भी लागू होता है, ध्यान में रखते हुए लक्ष्य के बाईं ओर क्रिया ऑपरेटर।
डेविड आर ट्रिबल

4
यदि आप i को बढ़ाने की कोशिश कर रहे हैं, तो i ++ और ++ i दोनों सही ढंग से आपके इरादे को व्यक्त करते हैं। एक के बाद एक को पसंद करने का कोई कारण नहीं है। एक पाठक के रूप में मुझे कोई अंतर नहीं दिखाई देता है, क्या आपके सहकर्मी i ++ देखने और सोचने के दौरान भ्रमित हो जाते हैं, हो सकता है कि यह एक टाइपो हो और वह मेरे लिए वेतन वृद्धि का मतलब नहीं था?
दान कार्टर

46

एक बेहतर उत्तर यह है कि ++iकभी-कभी तेज होगा लेकिन कभी धीमा नहीं होगा।

सभी को लगता है कि iयह एक नियमित रूप से निर्मित प्रकार है int। इस मामले में कोई औसत दर्जे का अंतर नहीं होगा।

हालांकि यदि iजटिल प्रकार है तो आप एक औसत दर्जे का अंतर पा सकते हैं। इसके लिए i++आपको वेतन वृद्धि से पहले अपनी कक्षा की एक प्रति बनानी होगी। एक कॉपी में शामिल होने के आधार पर यह वास्तव में धीमा हो सकता है क्योंकि ++itआप अंतिम मूल्य वापस कर सकते हैं।

Foo Foo::operator++()
{
  Foo oldFoo = *this; // copy existing value - could be slow
  // yadda yadda, do increment
  return oldFoo;
}

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

एक वास्तविक दुनिया का उदाहरण जहां यह हो सकता है, पुनरावृत्तियों का उपयोग होगा। अपने एप्लिकेशन में एक इट्रेटर को कॉपी करना एक बोतल-गर्दन होने की संभावना नहीं है, लेकिन परिणाम के प्रभावित होने के ++iबजाय उपयोग करने की आदत में आना अभी भी अच्छा अभ्यास है i++


36
प्रश्न में स्पष्ट रूप से C, C ++ का संदर्भ नहीं है।
डेन क्रिस्टोलोवेनु

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

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

@Pacerier प्रश्न को C, और C को ही टैग किया गया है। आप क्यों मानते हैं कि वे उस में रुचि नहीं रखते हैं? यह देखते हुए कि एसओ कैसे काम करता है, क्या यह मान लेना स्मार्ट नहीं होगा कि वे केवल जावा, सी #, कोबोल या किसी अन्य ऑफ-टॉपिक भाषा के बजाय सी में रुचि रखते हैं?
लुंडिन

18

संक्षिप्त जवाब:

गति के मामले में i++और इसके बीच कोई अंतर नहीं है ++i। एक अच्छे संकलक को दो मामलों में अलग-अलग कोड उत्पन्न नहीं करना चाहिए।

लंबा जवाब:

हर दूसरा उत्तर जो उल्लेख करने में विफल रहता है वह यह है कि ++iबनाम के बीच का अंतर i++केवल उस अभिव्यक्ति के भीतर समझ में आता है जो इसे मिली है।

के मामले में for(i=0; i<n; i++), i++अकेले अपनी अभिव्यक्ति में है: वहाँ से पहले एक अनुक्रम बिंदु है i++और इसके बाद एक है। इस प्रकार उत्पन्न एकमात्र मशीन कोड "वृद्धि iद्वारा 1" है और यह अच्छी तरह से परिभाषित है कि यह कार्यक्रम के बाकी हिस्सों के संबंध में कैसे अनुक्रमित है। तो तुम उपसर्ग के लिए इसे बदल जाएगा अगर ++, ऐसा नहीं बात थोड़ी सी में, आप अभी भी सिर्फ मशीन कोड "वृद्धि मिल जाएगा iद्वारा 1"।

बीच के अंतर ++iऔर i++केवल अभिव्यक्ति जैसे कि array[i++] = x;बनाम array[++i] = x;। कुछ तर्क दे सकते हैं और कह सकते हैं कि पोस्टफिक्स ऐसे ऑपरेशनों में धीमा होगा क्योंकि रजिस्टर जहां iरहता है उसे बाद में फिर से लोड करना पड़ता है। लेकिन फिर ध्यान दें कि कंपाइलर आपके निर्देशों को किसी भी तरह से आदेश देने के लिए स्वतंत्र है, जब तक कि यह "अमूर्त मशीन के व्यवहार को नहीं तोड़ता" जैसा कि सी मानक इसे कहते हैं।

इसलिए जब आप मान सकते हैं कि array[i++] = x;मशीन कोड में अनुवाद हो जाता है:

  • iरजिस्टर ए में स्टोर मूल्य ।
  • रजिस्टर बी में सरणी का स्टोर पता।
  • ए और बी जोड़ें, ए में परिणाम स्टोर करें।
  • A द्वारा दर्शाए गए इस नए पते पर, x का मान संग्रहीत करें।
  • iरजिस्टर ए // अक्षम के स्टोर मूल्य क्योंकि यहां अतिरिक्त निर्देश, हमने पहले ही एक बार ऐसा किया था।
  • वृद्धि रजिस्टर ए।
  • स्टोर रजिस्टर ए में i

संकलक अधिक कुशलता से कोड का उत्पादन कर सकता है, जैसे:

  • iरजिस्टर ए में स्टोर मूल्य ।
  • रजिस्टर बी में सरणी का स्टोर पता।
  • ए और बी जोड़ें, बी में परिणाम स्टोर करें।
  • वृद्धि रजिस्टर ए।
  • स्टोर रजिस्टर ए में i
  • ... // बाकी कोड।

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

इसलिए ++C. में उपसर्ग और उपसर्ग के बीच कोई अंतर नहीं है। अब आपको C प्रोग्रामर के रूप में क्या होना चाहिए, क्या ऐसे लोग हैं, जो असंगत रूप से कुछ मामलों में उपसर्ग का उपयोग करते हैं और अन्य मामलों में उपसर्ग करते हैं, बिना किसी तर्क के। इससे पता चलता है कि वे अनिश्चित हैं कि C कैसे काम करता है या उन्हें भाषा का गलत ज्ञान है। यह हमेशा एक बुरा संकेत है, यह बदले में सुझाव देता है कि वे अंधविश्वास या "धार्मिक हठधर्मिता" के आधार पर अपने कार्यक्रम में अन्य संदिग्ध निर्णय ले रहे हैं।

"उपसर्ग ++हमेशा तेज होता है" वास्तव में एक ऐसी झूठी हठधर्मिता है जो सी-प्रोग्रामर के बीच आम है।


3
किसी ने नहीं कहा "उपसर्ग ++ हमेशा तेज होता है"। वह गलत है। उन्होंने कहा कि "पोस्टफिक्स ++ कभी तेज नहीं होता है"।
पचेरियर

2
@Pacerier मैं किसी व्यक्ति विशेष को उद्धृत नहीं कर रहा हूं, लेकिन सिर्फ एक व्यापक, गलत धारणा है।
लंडिन

@rbaleksandar C ++! = C.
लुंडिन

@rbaleksandar प्रश्न और उत्तर दोनों सी + + के बारे में बात करते हैं। जो C से अलग है, क्योंकि इसमें ऑपरेटर ओवरलोडिंग और कॉपी कंस्ट्रक्टर आदि हैं
Lundin

3
@rbaleksandar c ++ == c && ++ c! = c
sadljkfhalskdjfh

17

स्कॉट मेयर्स से एक पत्ता लेना, अधिक प्रभावी c ++ आइटम 6: वेतन वृद्धि और क्षरण संचालन के उपसर्ग और उपसर्ग रूपों के बीच भेद

उपसर्ग संस्करण हमेशा वस्तुओं के संबंध में उपसर्ग पर पसंद किया जाता है, विशेष रूप से पुनरावृत्तियों के संबंध में।

इसका कारण यदि आप ऑपरेटरों के कॉल पैटर्न को देखते हैं।

// Prefix
Integer& Integer::operator++()
{
    *this += 1;
    return *this;
}

// Postfix
const Integer Integer::operator++(int)
{
    Integer oldValue = *this;
    ++(*this);
    return oldValue;
}

इस उदाहरण को देखते हुए यह देखना आसान है कि उपसर्ग ऑपरेटर हमेशा उपसर्ग से अधिक कुशल कैसे होगा। पोस्टफिक्स के उपयोग में एक अस्थायी वस्तु की आवश्यकता के कारण।

यही कारण है कि जब आप पुनरावृत्तियों का उपयोग करके उदाहरण देखते हैं तो वे हमेशा उपसर्ग संस्करण का उपयोग करते हैं।

लेकिन जैसा कि आप int के लिए बताते हैं कि संकलक अनुकूलन के कारण प्रभावी रूप से कोई अंतर नहीं है जो हो सकता है।


4
मुझे लगता है कि उनका प्रश्न C पर निर्देशित था, लेकिन C ++ के लिए आप बिल्कुल सही हैं, और इसके अलावा C लोगों को इसे अपनाना चाहिए क्योंकि वे C ++ के लिए भी इसका उपयोग कर सकते हैं। मैं अब तक अक्सर सी प्रोग्रामर पोस्टफिक्स सिंटैक्स का उपयोग करते हुए देखता हूं ;-)
एंडर्स रूने जेनसेन

-1 जबकि यह सी ++ प्रोग्रामर के लिए अच्छी सलाह है, यह उस सवाल का जवाब नहीं देता है, जिसे सी, टैग किया गया है। सी में, यह पूरी तरह से कोई फर्क नहीं पड़ता है कि आप उपसर्ग या पोस्टफिक्स का उपयोग करते हैं या नहीं।
लंडिन

1
एंड्रियास के उत्तर के अनुसार @ लुंडिन उपसर्ग और पोस्टिफ़िक्स सी में मायने रखते हैं । आप यह नहीं मान सकते हैं कि संकलक दूर जाएगा, और यह किसी भी भाषा के लिए अलवास के
JProgrammer

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

16

यदि आप माइक्रो ऑप्टिमाइज़ेशन के बारे में चिंतित हैं तो यहां एक अतिरिक्त अवलोकन है। घटते लूप्स संभवतः 'लूप' बढ़ाने से ज्यादा कुशल हो सकते हैं (निर्देश सेट आर्किटेक्चर जैसे एआरएम पर निर्भर करते हुए), दिए गए:

for (i = 0; i < 100; i++)

प्रत्येक लूप पर आपको प्रत्येक के लिए एक निर्देश होगा:

  1. जोड़ने 1के लिए i
  2. तुलना करें कि क्या iकिसी से कम है 100
  3. एक सशर्त शाखा यदि iएक से कम है 100

जबकि एक घटता लूप:

for (i = 100; i != 0; i--)

लूप में प्रत्येक के लिए एक निर्देश होगा:

  1. iसीपीयू रजिस्टर स्टेटस फ्लैग सेट करना, घटाना ।
  2. सीपीयू रजिस्टर स्थिति ( Z==0) के आधार पर एक सशर्त शाखा ।

बेशक यह केवल तभी काम करता है जब शून्य में कमी हो!

एआरएम सिस्टम डेवलपर की गाइड से याद किया गया।


अच्छा था। लेकिन क्या यह कम कैश हिट नहीं बनाता है?
एंड्रियास

कोड अनुकूलन पुस्तकों से एक पुरानी अजीब चाल है, वेतन वृद्धि वाले पते के साथ शून्य-परीक्षण शाखा का लाभ। यहां उच्च स्तरीय भाषा में उदाहरण दिया गया है (शायद बेकार है, क्योंकि कई कंपाइलर इसे कम कुशल लेकिन अधिक सामान्य लूप कोड के साथ बदलने के लिए काफी स्मार्ट हैं : int [N]; for (i = -N; i; ++ i) एक [N + i] + = 123;
दोपहर

@ क्या आप संभवतः यह बता सकते हैं कि आप किन कोड अनुकूलन पुस्तकों का संदर्भ देते हैं? मैंने अच्छे लोगों को खोजने के लिए संघर्ष किया है।
मेज़ामोर्फिक

7
यह उत्तर प्रश्न का उत्तर नहीं है।
मोनोक्रोम

@ 86 के लिए @mezamorphic मेरे स्रोत हैं: इंटेल ऑप्टिमाइज़ेशन मैनुअल, एग्नर फॉग, पॉल हेशेह, माइकल अबाश।
noop

11

कृपया "कौन सा तेज़ है" का प्रश्न तय नहीं करना चाहिए कि किसका उपयोग करना है। संभावना है कि आप कभी भी इतना ध्यान रखने वाले नहीं हैं, और इसके अलावा, प्रोग्रामर पढ़ने का समय मशीन के समय से कहीं अधिक महंगा है।

जो भी मानव कोड पढ़ने के लिए सबसे अधिक समझ में आता है का उपयोग करें।


3
मेरा मानना ​​है कि वास्तविक दक्षता लाभ और इरादे की समग्र स्पष्टता के लिए अस्पष्ट पठनीयता सुधार को प्राथमिकता देना गलत है।
noop

4
मेरा शब्द "प्रोग्रामर रीडिंग टाइम" मोटे तौर पर "मंशा की स्पष्टता" के अनुरूप है। "वास्तविक दक्षता लाभ" अक्सर अथाह होते हैं, शून्य के करीब पर्याप्त उन्हें शून्य कहते हैं। ओपी के मामले में, जब तक कि कोड को यह पता लगाने के लिए प्रोफाइल नहीं किया गया है कि ++ i एक अड़चन है, जिसके बारे में सवाल यह है कि यह तेज है और प्रोग्रामर विचार इकाइयों की बर्बादी है।
एंडी लेस्टर

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

तुम वही कह रहे हो जो मैं कह रहा हूं। "दक्षता" के बारे में चिंता करने के बजाय इरादे दिखाना और पठनीयता में सुधार करना अधिक महत्वपूर्ण है।
एंडी लेस्टर

2
मैं अब भी सहमत नहीं हो सकता। यदि पठनीयता आपकी प्राथमिकताओं की सूची में अधिक है, तो हो सकता है कि प्रोग्रामिंग भाषा की आपकी पसंद गलत हो। C / C ++ प्राथमिक उद्देश्य कुशल कोड लिख रहा है।
noop

11

सबसे पहले: C के बीच अंतर i++और ++iनापाक है।


विवरण के लिए।

1. अच्छी तरह से ज्ञात सी ++ मुद्दा: ++iतेज है

C ++ में, ++iअधिक कुशल iff iकिसी तरह की वस्तु है जो ओवरलोडेड वेतन वृद्धि ऑपरेटर के साथ है।

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

जैसा कि ऊपर उल्लेख किया गया है, यह मौलिक प्रकारों पर लागू नहीं होता है।

2. अल्पज्ञात तथ्य: तेज i++ हो सकता है

यदि किसी भी निर्माता / विध्वंसक को बुलाने की आवश्यकता नहीं है, जो कि सी में हमेशा होता है, ++iऔर i++समान रूप से तेज़ होना चाहिए, है ना? नहीं। वे वस्तुतः समान रूप से तेज़ हैं, लेकिन छोटे अंतर हो सकते हैं, जो अधिकांश अन्य उत्तरदाताओं को गलत तरीके से मिला।

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


आपकी बात 2 के बारे में: यदि ++iया i++का उपयोग किसी अन्य अभिव्यक्ति के भीतर किया जाता है, तो उनके बीच बदलने से अभिव्यक्ति के शब्दार्थ बदल जाते हैं, इसलिए किसी भी संभावित प्रदर्शन लाभ / हानि प्रश्न से बाहर है। यदि वे स्टैंडअलोन हैं, अर्थात, ऑपरेशन का परिणाम तुरंत उपयोग नहीं किया जाता है, तो कोई भी सभ्य संकलक इसे उसी चीज़ के लिए संकलित करेगा, उदाहरण के लिए एक INCविधानसभा निर्देश।
शहबाज

1
@ शहबाज यह पूरी तरह सच है, लेकिन बात नहीं है। 1) भले ही अर्थ विज्ञान अलग हैं, दोनों i++और ++iएक-एक करके पाश स्थिरांक का समायोजन करके लगभग हर संभव स्थिति में दूसरे के स्थान पर इस्तेमाल किया जा सकता है ताकि वे क्या वे प्रोग्रामर के लिए कर में पास बराबर हैं। 2) भले ही दोनों एक ही निर्देश के लिए संकलित हों, लेकिन उनका निष्पादन सीपीयू के लिए अलग है। के मामले में i++, सीपीयू समान मूल्य का उपयोग करने वाले कुछ अन्य निर्देशों के समानांतर में वेतन वृद्धि की गणना कर सकता है (सीपीयू वास्तव में ऐसा करता है!), जबकि ++iसीपीयू को वेतन वृद्धि के बाद अन्य निर्देश को शेड्यूल करना होगा ।
cmaster -

4
@ शहबाज एक उदाहरण के रूप में: if(++foo == 7) bar();और if(foo++ == 6) bar();कार्यात्मक रूप से समकक्ष हैं। हालांकि, दूसरा एक चक्र तेजी से हो सकता है, क्योंकि तुलना और वेतन वृद्धि को सीपीयू द्वारा समानांतर में गणना की जा सकती है। ऐसा नहीं है कि यह एकल चक्र बहुत मायने रखता है, लेकिन अंतर वहाँ है।
विस्फ़ोटक -

1
अच्छी बात। स्थिरांक बहुत अधिक दिखाते हैं (साथ ही <उदाहरण के लिए बनाम <=) जहां ++आमतौर पर उपयोग किया जाता है, इसलिए हालांकि बीच का रूपांतरण अक्सर आसानी से संभव है।
शाहबाज

1
मुझे बिंदु 2 पसंद है, लेकिन यह केवल तभी लागू होता है जब मूल्य का उपयोग किया जाता है, righ? प्रश्न कहता है "यदि परिणामी मूल्य का उपयोग नहीं किया जाता है?", तो यह भ्रमित हो सकता है।
जिनावी

7

@Mark भले ही कंपाइलर को वैरिएबल की स्टैक आधारित (स्टैक बेस्ड) अस्थायी कॉपी करने की अनुमति है और gcc (हाल के संस्करणों में) ऐसा कर रहा है, इसका मतलब यह नहीं है कि सभी कंपाइलर हमेशा ऐसा करेंगे।

मैंने अभी इसे अपने वर्तमान प्रोजेक्ट में उपयोग किए गए कंपाइलरों के साथ परीक्षण किया और 4 में से 3 इसे ऑप्टिमाइज़ नहीं करते हैं।

कभी भी यह न समझें कि कंपाइलर सही हो जाता है, खासकर यदि संभवतः तेज, लेकिन कभी भी धीमा कोड पढ़ने में उतना आसान नहीं है।

यदि आपके पास अपने कोड में किसी एक ऑपरेटर का वास्तव में बेवकूफ कार्यान्वयन नहीं है:

Alwas पसंद करता है ++ i over i ++।


बस जिज्ञासु ... आप एक परियोजना में 4 अलग सी संकलक का उपयोग क्यों करते हैं? या एक टीम या एक कंपनी में, उस मामले के लिए?
लॉरेंस डोल

3
हर प्लेटफ़ॉर्म के लिए गेम बनाते समय यह अपना कंपाइलर / टूलचैन लाता है। एक आदर्श दुनिया में हम सभी लक्ष्यों के लिए gcc / clang / llvm का उपयोग कर सकते हैं, लेकिन इस दुनिया में हमें Microsoft, Intel, Metroworks, Sony इत्यादि के साथ जुड़ना होगा
Andreas

5

सी में, कंपाइलर आम तौर पर उन्हें एक ही होने के लिए ऑप्टिमाइज़ कर सकता है यदि परिणाम अप्रयुक्त हो।

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


4

मैं ऐसी स्थिति के बारे में सोच सकता हूं जहां उपसर्ग वृद्धि की तुलना में पोस्टफिक्स धीमा है:

कल्पना करें कि रजिस्टर के साथ एक प्रोसेसर Aको संचायक के रूप में उपयोग किया जाता है और यह कई निर्देशों में उपयोग किया जाने वाला एकमात्र रजिस्टर है (कुछ छोटे माइक्रोकंट्रोलर वास्तव में इस तरह हैं)।

अब एक काल्पनिक विधानसभा में निम्नलिखित कार्यक्रम और उनके अनुवाद की कल्पना करें:

उपसर्ग वृद्धि:

a = ++b + c;

; increment b
LD    A, [&b]
INC   A
ST    A, [&b]

; add with c
ADD   A, [&c]

; store in a
ST    A, [&a]

उपसर्ग वृद्धि:

a = b++ + c;

; load b
LD    A, [&b]

; add with c
ADD   A, [&c]

; store in a
ST    A, [&a]

; increment b
LD    A, [&b]
INC   A
ST    A, [&b]

ध्यान दें कि कैसे मान bको फिर से लोड करने के लिए मजबूर किया गया था। उपसर्ग वृद्धि के साथ, कंपाइलर मान को बढ़ा सकता है और इसका उपयोग करने के साथ आगे बढ़ सकता है, संभवतः इसे फिर से लोड करने से बचें क्योंकि वांछित मूल्य पहले से ही वृद्धि के बाद रजिस्टर में है। हालाँकि, पोस्टफिक्स इन्क्रीमेंट के साथ, कंपाइलर को दो मानों में से एक, एक पुराना और एक बढ़े हुए मूल्य से निपटना पड़ता है, जैसा कि मैं एक और मेमोरी एक्सेस में उपरोक्त परिणाम दिखाता हूं।

बेशक, अगर वेतन वृद्धि का उपयोग नहीं किया जाता है, जैसे कि एक एकल i++;बयान, संकलक (और करता है) केवल पोस्टफ़िक्स या उपसर्ग उपयोग की परवाह किए बिना एक वेतन वृद्धि निर्देश उत्पन्न कर सकता है।


एक साइड नोट के रूप में, मैं यह उल्लेख करना चाहता हूं कि एक अभिव्यक्ति जिसमें कोई भी अतिरिक्त प्रयास के बिना b++एक के साथ परिवर्तित नहीं किया जा सकता है ++b(उदाहरण के लिए जोड़कर - 1)। इसलिए दोनों की तुलना अगर वे किसी अभिव्यक्ति का हिस्सा हैं तो वास्तव में मान्य नहीं हैं। अक्सर, जहां आप b++एक अभिव्यक्ति के अंदर उपयोग करते हैं, जिसका आप उपयोग नहीं कर सकते हैं ++b, इसलिए भले ही ++bसंभावित रूप से अधिक कुशल हों, यह बस गलत होगा। अपवाद निश्चित रूप से है यदि अभिव्यक्ति इसके लिए भीख माँग रही है (उदाहरण के लिए a = b++ + 1;जिसे बदला जा सकता है a = ++b;)।


4

मैं यहाँ ज्यादातर जवाबों और टिप्पणियों के माध्यम से पढ़ता रहा हूं, और मुझे एक भी उदाहरण नहीं दिखाई दिया, जिसके बारे में मैं सोच सकता था कि यह कहां i++से अधिक कुशल है ++i(और शायद आश्चर्यजनक रूप --i से अधिक कुशल थाi-- )। यह DEC PDP-11 के लिए C कंपाइलर के लिए है!

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

यह स्पष्ट रूप से एक बहुत ही गूढ़ उदाहरण है, लेकिन (या मुझे कहना चाहिए यह अपवाद है, जहां के बाद वेतन वृद्धि और अधिक कुशल है प्रदान करता था , के बाद से वहाँ PDP-11 सी कोड के लिए इन दिनों ज्यादा मांग नहीं है)।


2
बहुत गूढ़, लेकिन बहुत दिलचस्प!
मार्क हैरिसन

@daShier। +1, हालांकि मैं असहमत हूं, लेकिन यह गूढ़ नहीं है, या कम से कम यह नहीं होना चाहिए। 70 के दशक के प्रारंभ में पीडीपी -11 लक्ष्य प्रोसेसर था, सी एटी एंड टी बेल लैब्स में यूनिक्स के साथ सह-विकसित किया गया था। इस युग के बाद के वेतन वृद्धि के यूनिक्स स्रोत कोड में, "i ++", आंशिक रूप से अधिक प्रचलित है क्योंकि डेवलपर्स को पता था कि मान कब सौंपा गया है, "j = i ++", या एक सूचकांक के रूप में उपयोग किया जाता है, "a [i ++] ="। कोड थोड़ा तेज (और छोटा) होगा। ऐसा लगता है कि जब तक पूर्व आवश्यकता नहीं थी तब तक वे पोस्ट इंक्रीमेंट का उपयोग करने की आदत में पड़ गए। दूसरों ने उनके कोड को पढ़कर सीखा और इस आदत को भी उठाया।
जिम्हार्क

68000 में एक ही सुविधा है, पोस्ट-इन्क्रीमेंट और प्री-डेक्रिमेशन हार्डवेयर में (एड्रेसिंग मोड्स के रूप में) समर्थित हैं, लेकिन दूसरे तरीके से नहीं। मोटोरोला टीम um, DEC PDP-11 से प्रेरित थी।
जिम्हार्क

2
@ जिम्हार्क, हां, मैं उन पीडीपी -11 प्रोग्रामरों में से एक हूं, जिन्होंने 68000 में संक्रमण किया और अभी भी उपयोग कर रहे हैं --iऔर i++
दाशियर

2

मैं हमेशा पूर्व वेतन वृद्धि पसंद करता हूँ, लेकिन ...

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

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

इसके अलावा, ऑप्टिमाइज़र को कम करने की संभावना से मतलब है कि कंपाइलर तेजी से चलता है।


आपकी पोस्टिंग C ++ - विशिष्ट है जबकि प्रश्न C. के बारे में है। किसी भी मामले में, आपका उत्तर गलत है: कस्टम पोस्ट-इन्क्रीमेंट ऑपरेटरों के लिए कंपाइलर आमतौर पर कुशल कोड के रूप में उत्पादन करने में सक्षम नहीं होगा।
कोनराड रुडोल्फ

वह बताता है कि "यदि फ़ंक्शन इनबिल्ड हो जाता है", और इससे उसका तर्क सही हो जाता है।
ब्लिसॉर्बलेड

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

0

मेरा सी थोड़ा रूखा है, इसलिए मैं पहले से माफी मांगता हूं। स्पीडवाइज, मैं परिणामों को समझ सकता हूं। लेकिन, मुझे भ्रम है कि दोनों फाइलें एक ही MD5 हैश में कैसे निकलीं। हो सकता है कि लूप के लिए एक ही रन हो, लेकिन निम्नलिखित 2 लाइनों के कोड अलग-अलग विधानसभा उत्पन्न नहीं करेंगे?

myArray[i++] = "hello";

बनाम

myArray[++i] = "hello";

पहले वाला एरे को मान लिखता है, फिर वेतन वृद्धि i। दूसरी वेतन वृद्धि तो मैं सरणी के लिए लिखता हूं। मैं कोई असेंबली विशेषज्ञ नहीं हूं, लेकिन मैं अभी यह नहीं देखता कि कोड के इन 2 अलग-अलग लाइनों से एक ही निष्पादन योग्य कैसे उत्पन्न होगा।

केवल मेरे दो सेंट्स।


1
@ जैसन जेड कंपाइलर ऑप्टिमाइज़ेशन असेंबली समाप्त होने से पहले होता है, यह देखता है कि i वैरिएबल का उपयोग उसी लाइन पर कहीं और नहीं किया गया है, इसलिए इसका मान रखना एक बेकार होगा, यह संभवतः इसे प्रभावी रूप से i ++ में फ़्लिप करता है। लेकिन यह सिर्फ एक अनुमान है। मैं तब तक इंतजार नहीं कर सकता जब तक मेरा कोई व्याख्याता यह कहने की कोशिश नहीं करता कि यह तेजी से हो रहा है और मुझे वह व्यक्ति बनना है, जो व्यावहारिक सबूतों के साथ एक सिद्धांत को सही करता है। मैं लगभग पहले से ही दुश्मनी महसूस कर सकता हूं ^ _ ^
Tarks

3
"मेरा सी थोड़ा कठोर है, इसलिए मैं पहले से माफी मांगता हूं।" आपके C के साथ कुछ भी गलत नहीं है, लेकिन आपने मूल प्रश्न को पूरी तरह से नहीं पढ़ा है: "क्या परिणामस्वरूप परिणाम का उपयोग नहीं होने पर i ++ और ++ के बीच एक प्रदर्शन अंतर है? " इटालिक्स पर ध्यान दें। अपने उदाहरण, 'परिणाम' की मैं ++ / ++ मैं में है , लेकिन पाश के लिए मुहावरेदार में, पूर्व / postincrement ऑपरेटर की 'परिणाम' तो संकलक क्या कर सकते हैं यह क्या पसंद करती है नहीं किया जाता है।
रोडी

2
आपके उदाहरण में कोड अलग होगा, क्योंकि आप मूल्य का उपयोग कर रहे हैं। उदाहरण जहां वे समान थे वे केवल वेतन वृद्धि के लिए ++ का उपयोग कर रहे थे, और या तो लौटाए गए मूल्य का उपयोग नहीं कर रहे थे।
जॉन गार्डनर

1
फिक्स: "कंपाइलर यह देखेगा कि i ++ का रिटर्न वैल्यू उपयोग नहीं किया गया है, इसलिए यह इसे ++ i को फ्लिप करेगा"। आपने जो लिखा है वह भी गलत है क्योंकि आप एक साथ i ++, i ++ एक ही लाइन (स्टेटमेंट) पर नहीं कर सकते हैं, इसका परिणाम अपरिभाषित है।
ब्लिसॉर्बलेड

1
बदलने foo[i++]के लिए foo[++i]कुछ और बदल रहा है स्पष्ट रूप से कार्यक्रम अर्थ विज्ञान बदल जाएगा बिना, लेकिन एक पाश-उत्थापन अनुकूलन तर्क के बिना एक संकलक का उपयोग करते समय कुछ प्रोसेसर पर, incrementing pऔर qएक बार और फिर एक पाश जो प्रदर्शन जैसे चल रहा है *(p++)=*(q++);तेजी से एक पाश जो प्रदर्शन का उपयोग करने से हो सकता है *(++pp)=*(++q);। कुछ प्रोसेसर पर बहुत तंग छोरों के लिए गति अंतर महत्वपूर्ण (10% से अधिक) हो सकता है, लेकिन यह संभवतः सी में एकमात्र मामला है जहां पोस्ट-इंक्रीमेंट पूर्व-वेतन वृद्धि की तुलना में तेजी से होता है।
सुपरकाट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.