क्या <= से तेज है?


1574

है if( a < 901 )तेजी से if( a <= 900 )

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


153
मुझे कोई कारण नहीं है कि इस प्रश्न को बंद कर दिया जाना चाहिए (और विशेष रूप से हटाए नहीं गए, क्योंकि वोट वर्तमान में दिखाई दे रहे हैं) इसका ऐतिहासिक महत्व, उत्तर की गुणवत्ता और इस तथ्य को देखते हुए कि प्रदर्शन में अन्य शीर्ष प्रश्न खुले हैं। कम से कम इसे बंद कर दिया जाना चाहिए। इसके अलावा, भले ही प्रश्न स्वयं गलत / भोला हो, तथ्य यह है कि यह एक पुस्तक में दिखाई दिया है इसका मतलब है कि मूल गलत सूचना कहीं न कहीं "विश्वसनीय" स्रोतों में मौजूद है, और यह सवाल इसलिए रचनात्मक है कि यह स्पष्ट करने में मदद करता है।
जेसन सी

32
आपने हमें कभी नहीं बताया कि आप किस पुस्तक का उल्लेख कर रहे हैं।
जोनाथन रेनहार्ट

159
टाइपिंग <की तुलना में टाइपिंग दो गुना तेज है <=
डिंगकिंग

6
यह 8086 पर सच था।
यहोशू

7
उत्थान की संख्या स्पष्ट रूप से दर्शाती है कि ऐसे सैकड़ों लोग हैं जो भारी रूप से अपनाते हैं।
m93a

जवाबों:


1703

नहीं, यह अधिकांश आर्किटेक्चर पर तेज़ नहीं होगा। आपने निर्दिष्ट नहीं किया, लेकिन x86 पर, सभी अभिन्न तुलनाओं को आमतौर पर दो मशीन निर्देशों में लागू किया जाएगा:

  • एक testया cmpनिर्देश, जो सेट करता हैEFLAGS
  • और तुलना प्रकार (और कोड लेआउट) के आधार पर एक Jcc(छलांग) निर्देश :
    • jne - नहीं के बराबर कूदें -> ZF = 0
    • jz - शून्य (बराबर) -> कूदें ZF = 1
    • jg - अधिक होने पर कूदें -> ZF = 0 and SF = OF
    • (आदि...)

उदाहरण (संक्षिप्तता के लिए संपादित) के साथ संकलित$ gcc -m32 -S -masm=intel test.c

    if (a < b) {
        // Do something 1
    }

इसके संकलन:

    mov     eax, DWORD PTR [esp+24]      ; a
    cmp     eax, DWORD PTR [esp+28]      ; b
    jge     .L2                          ; jump if a is >= b
    ; Do something 1
.L2:

तथा

    if (a <= b) {
        // Do something 2
    }

इसके संकलन:

    mov     eax, DWORD PTR [esp+24]      ; a
    cmp     eax, DWORD PTR [esp+28]      ; b
    jg      .L5                          ; jump if a is > b
    ; Do something 2
.L5:

इसलिए दोनों के बीच एकमात्र अंतर jgबनाम एक jgeनिर्देश है। दोनों को समान समय लगेगा।


मैं टिप्पणी को संबोधित करना चाहता हूं कि कुछ भी इंगित नहीं करता है कि विभिन्न कूद निर्देश समय की एक ही राशि लेते हैं। यह जवाब देने के लिए थोड़ा मुश्किल है, लेकिन यहां मैं क्या दे सकता हूं: इंटेल इंस्ट्रक्शन सेट संदर्भ में , वे सभी एक सामान्य निर्देश के तहत एक साथ समूहीकृत हैं, Jcc(कूदो यदि स्थिति पूरी हो गई है)। एक ही समूहन को परिशिष्ट संदर्भ नियमावली के तहत परिशिष्ट C. लेटेंसी और थ्रूपुट में एक साथ किया जाता है।

विलंबता - निष्पादन कोर के लिए आवश्यक सभी घड़ी चक्रों की संख्या जो एक निर्देश बनाने वाले सभी ofops के निष्पादन को पूरा करने के लिए आवश्यक हैं।

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

इसके लिए मान Jccनिम्न हैं:

      Latency   Throughput
Jcc     N/A        0.5

निम्नलिखित फुटनोट के साथ Jcc:

7) सशर्त कूद निर्देशों का चयन शाखाओं की भविष्यवाणी की स्थिति में सुधार करने के लिए अनुभाग 3.4.1, “शाखा भविष्यवाणी अनुकूलन” की सिफारिश पर आधारित होना चाहिए। जब शाखाओं की सफलतापूर्वक भविष्यवाणी की जाती है, तो इसका विलंबता jccप्रभावी रूप से शून्य होता है।

इसलिए, इंटेल डॉक्स में कुछ भी कभी Jccभी एक निर्देश को दूसरों से अलग नहीं मानता है।

यदि कोई निर्देशों को लागू करने के लिए उपयोग किए जाने वाले वास्तविक सर्किटरी के बारे में सोचता है, तो यह मान सकता है कि EFLAGSशर्तों को पूरा करने के लिए अलग-अलग बिट्स पर सरल और / या द्वार होंगे । फिर, कोई कारण नहीं है कि दो बिट्स के परीक्षण का एक निर्देश केवल एक परीक्षण की तुलना में अधिक या कम समय लेना चाहिए (गेट प्रचार विलंब में देरी, जो घड़ी की अवधि की तुलना में बहुत कम है।)


संपादित करें: फ्लोटिंग पॉइंट

यह x87 फ्लोटिंग पॉइंट के लिए भी सही है: (ऊपर की तरह बहुत ही कोड, लेकिन doubleइसके बजाय int।)

        fld     QWORD PTR [esp+32]
        fld     QWORD PTR [esp+40]
        fucomip st, st(1)              ; Compare ST(0) and ST(1), and set CF, PF, ZF in EFLAGS
        fstp    st(0)
        seta    al                     ; Set al if above (CF=0 and ZF=0).
        test    al, al
        je      .L2
        ; Do something 1
.L2:

        fld     QWORD PTR [esp+32]
        fld     QWORD PTR [esp+40]
        fucomip st, st(1)              ; (same thing as above)
        fstp    st(0)
        setae   al                     ; Set al if above or equal (CF=0).
        test    al, al
        je      .L5
        ; Do something 2
.L5:
        leave
        ret

239
@Dyppl वास्तव में jgऔर jnleएक ही निर्देश हैं, 7F:-)
जोनाथन

17
यह उल्लेख करने के लिए कि ऑप्टिमाइज़र कोड को संशोधित कर सकता है यदि वास्तव में एक विकल्प दूसरे की तुलना में तेज है।
एल्जार लीबोविच

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

22
@jontejj मैं उससे बहुत परिचित हूं। क्या आपने मेरा जवाब भी पढ़ा ? मैं राज्य में एक ही बारे में कुछ भी नहीं किया था संख्या निर्देश की, मैं ने कहा कि वे अनिवार्य रूप से ठीक उसी को संकलित किया गया है instrutcions , सिवाय एक कूद अनुदेश एक ध्वज में दिख रही है, और अन्य कूद अनुदेश दो झंडे में दिख रही है। मेरा मानना ​​है कि मैंने यह दिखाने के लिए पर्याप्त साक्ष्य दिए हैं कि वे शब्दार्थ समान हैं।
जोनाथन रेनहार्ट

2
@jontejj आप बहुत अच्छी बात करते हैं। इस उत्तर के लिए जितनी अधिक दृश्यता हो, मुझे संभवतः इसे थोड़ी सफाई देनी चाहिए। प्रतिक्रिया के लिए धन्यवाद।
जोनाथन रेनहार्ट

593

ऐतिहासिक रूप से (हम 1980 के दशक और 1990 के दशक की शुरुआत में बात कर रहे थे), कुछ वास्तुशिल्प थे जिनमें यह सच था। मूल मुद्दा यह है कि पूर्णांक तुलना अंतर्निहित पूर्णांक के माध्यम से स्वाभाविक रूप से कार्यान्वित की जाती है। यह निम्नलिखित मामलों को जन्म देता है।

Comparison     Subtraction
----------     -----------
A < B      --> A - B < 0
A = B      --> A - B = 0
A > B      --> A - B > 0

अब, जब A < Bघटाव को घटाव के लिए उच्च-बिट को सही होने के लिए उधार लेना पड़ता है, जैसे आप ले जाते हैं और हाथ से जोड़ते और घटाते समय उधार लेते हैं। इस "उधार" बिट को आमतौर पर कैरी बिट के रूप में संदर्भित किया जाता था और एक शाखा निर्देश द्वारा परीक्षण किया जाएगा। शून्य बिट नामक एक दूसरा बिट सेट किया जाएगा यदि घटाव पहचान शून्य था जो समानता को निहित करता है।

आमतौर पर कम से कम दो सशर्त शाखा निर्देश होते थे, एक कैरी बिट पर शाखा और दूसरा शून्य बिट पर।

अब, मामले के दिल में लाने के लिए, ले जाने और शून्य बिट परिणामों को शामिल करने के लिए पिछली तालिका का विस्तार करें।

Comparison     Subtraction  Carry Bit  Zero Bit
----------     -----------  ---------  --------
A < B      --> A - B < 0    0          0
A = B      --> A - B = 0    1          1
A > B      --> A - B > 0    1          0

तो, के लिए एक शाखा को लागू करना A < Bएक निर्देश में किया जा सकता है, क्योंकि कैरी बिट केवल इस मामले में स्पष्ट है, अर्थात्।

;; Implementation of "if (A < B) goto address;"
cmp  A, B          ;; compare A to B
bcz  address       ;; Branch if Carry is Zero to the new address

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

;; Implementation of "if (A <= B) goto address;"
cmp A, B           ;; compare A to B
bcz address        ;; branch if A < B
bzs address        ;; also, Branch if the Zero bit is Set

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


10
इसके अतिरिक्त, x86 जैसे आर्किटेक्चर निर्देशों को लागू करते हैं jge, जो शून्य और साइन / फ्लैग दोनों का परीक्षण करते हैं।
ग्रेफेड

3
भले ही यह किसी दिए गए आर्किटेक्चर के लिए सही हो। वे कौन सी बाधाएँ हैं जो संकलक लेखकों में से किसी ने भी नहीं देखीं, और धीमी के साथ तेजी से बदलने के लिए एक अनुकूलन जोड़ा?
जॉन हैना

8
यह 8080 पर सच है। इसमें शून्य पर कूदने और माइनस पर कूदने के निर्देश हैं, लेकिन दोनों में से कोई भी एक साथ परीक्षण नहीं कर सकता है।

4
यह 6502 और 65816 प्रोसेसर परिवार पर भी मामला है, जो मोटोरोला 68HC11 / 12 तक फैला हुआ है।
लुकास

31
यहां तक ​​कि 8080 पर एक <=परीक्षण को एक निर्देश में लागू किया जा सकता है जिसमें ऑपरेंड्स की अदला-बदली और परीक्षण not <(इसके बराबर >=) यह <=स्वैप किए गए ऑपरेंड के साथ वांछित है cmp B,A; bcs addr:। यही कारण है कि यह परीक्षण इंटेल द्वारा छोड़ा गया था, उन्होंने इसे बेमानी माना और आप उस समय निरर्थक निर्देशों को बर्दाश्त नहीं कर सके :-)
गनथर पाईज़

92

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

अगर वहाँ कुछ मंच कहाँ था <तेजी से था <=सरल पूर्णांक प्रकार के लिए, संकलक चाहिए हमेशा कन्वर्ट <=करने के लिए <स्थिरांक के लिए। कोई भी कंपाइलर जो सिर्फ एक बुरा कंपाइलर (उस प्लेटफॉर्म के लिए) नहीं होगा।


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

अभी भी कुछ बढ़त मामलों में जहां एक तुलना एक निरंतर मूल्य होने के नीचे धीमी हो सकता है <=, जैसे, जब से परिवर्तन (a < C)करने के लिए (a <= C-1)(कुछ निरंतर के लिए C) का कारण बनता है Cअनुदेश सेट में एन्कोड करने के लिए और अधिक कठिन हो सकता है। उदाहरण के लिए, एक निर्देश सेट तुलना में एक कॉम्पैक्ट रूप में हस्ताक्षरित स्थिरांक -127 से 128 तक का प्रतिनिधित्व करने में सक्षम हो सकता है, लेकिन उस सीमा के बाहर स्थिरांक को एक लंबी, धीमी एन्कोडिंग या पूरी तरह से एक अन्य निर्देश का उपयोग करके लोड करना पड़ता है। तो तुलना की तरह (a < -127)एक सीधा परिवर्तन नहीं हो सकता है।
BeeOnRope

@BeeOnRope मुद्दा यह नहीं था कि क्या अलग-अलग स्थिरांक होने के कारण अलग-अलग होने वाले ऑपरेशन प्रदर्शन को प्रभावित कर सकते हैं, लेकिन क्या अलग-अलग स्थिरांक का उपयोग करके एक ही ऑपरेशन को व्यक्त करना प्रदर्शन को प्रभावित कर सकता है। इसलिए हम की तुलना नहीं कर रहे हैं करने के लिए है क्योंकि आप कोई विकल्प नहीं देखते है, तो आप एक की जरूरत का उपयोग करें। हम तुलना कर रहे हैं करने के लिए है, जो विभिन्न एन्कोडिंग या अलग निर्देश की आवश्यकता नहीं कर सकते हैं क्योंकि वे एक ही सत्य तालिका है। किसी एक का एन्कोडिंग समान रूप से दूसरे का एन्कोडिंग है। a > 127a > 128a > 127a >= 128
डेविड श्वार्ट्ज

मैं अपने बयान "अगर वहाँ कुछ मंच जहां [<= धीमी थी] संकलक हमेशा परिवर्तित करना चाहिए था करने के लिए एक सामान्य तरीके से जवाब दिया गया था <=करने के लिए <स्थिरांक के लिए"। जहाँ तक मुझे पता है, उस परिवर्तन में निरंतर परिवर्तन शामिल है। उदाहरण के लिए, a <= 42संकलित किया गया है a < 43क्योंकि <तेज है। कुछ किनारे के मामलों में, इस तरह का परिवर्तन फलदायी नहीं होगा क्योंकि नए निरंतर को अधिक या धीमी निर्देशों की आवश्यकता हो सकती है। बेशक a > 127और a >= 128समतुल्य हैं और एक संकलक को दोनों रूपों को (एक ही) सबसे तेज़ तरीके से सांकेतिक शब्दों में बदलना चाहिए, लेकिन मैंने जो कहा है, उसके साथ यह असंगत नहीं है।
मधुमक्खी पालन

67

मैं देखता हूं कि न तो तेज है। कंपाइलर प्रत्येक मशीन में एक ही मशीन कोड अलग मूल्य के साथ उत्पन्न करता है।

if(a < 901)
cmpl  $900, -4(%rbp)
jg .L2

if(a <=901)
cmpl  $901, -4(%rbp)
jg .L3

मेरा उदाहरण ifलिनक्स पर G86 से x86_64 प्लेटफॉर्म पर है।

कंपाइलर लेखक बहुत स्मार्ट लोग हैं, और वे इन चीजों के बारे में सोचते हैं और कई अन्य जिन्हें हम में से अधिकांश लोग मानते हैं।

मैंने देखा कि यदि यह स्थिर नहीं है, तो एक ही मशीन कोड किसी भी स्थिति में उत्पन्न होता है।

int b;
if(a < b)
cmpl  -4(%rbp), %eax
jge   .L2

if(a <=b)
cmpl  -4(%rbp), %eax
jg .L3

9
ध्यान दें कि यह x86 के लिए विशिष्ट है।
माइकल पेट्रोत्ता

10
मुझे लगता है कि आपको if(a <=900)यह दिखाने के लिए उपयोग करना चाहिए कि यह वास्तव में समान asm उत्पन्न करता है :)
Lipis

2
@AdrianCornish क्षमा करें .. मैंने इसे संपादित किया है .. यह कमोबेश वैसा ही है .. लेकिन यदि आप दूसरे को बदलते हैं तो <= 900 पर तो asm कोड बिल्कुल वैसा ही होगा :) अब यह बहुत ज्यादा है .. लेकिन आप पता है .. OCD के लिए :)
लिपिस

3
@Boann कि अगर (सच) को कम करने और पूरी तरह से समाप्त हो सकता है।
Qsario

5
किसी ने यह नहीं बताया कि यह अनुकूलन केवल निरंतर तुलना पर लागू होता है । मैं गारंटी दे सकता हूं कि यह दो चर की तुलना के लिए इस तरह से नहीं किया जाएगा।
जोनाथन रेनहार्ट

51

फ्लोटिंग पॉइंट कोड के लिए, आधुनिक आर्किटेक्चर पर <= तुलना वास्तव में धीमी (एक निर्देश द्वारा) हो सकती है। यहाँ पहला कार्य है:

int compare_strict(double a, double b) { return a < b; }

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

अब इस फ़ंक्शन पर विचार करें:

int compare_loose(double a, double b) { return a <= b; }

इसके लिए compare_strictऊपर के समान कार्य की आवश्यकता है , लेकिन अब इसमें दो बिट्स हैं: "से कम" और "के बराबर था।" इसके लिए crorइन दोनों बिट्स को एक में मिलाने के लिए एक अतिरिक्त निर्देश ( - कंडीशन रजिस्टर बिटवाइज़ OR) की आवश्यकता होती है। इसलिए compare_looseपांच निर्देशों की आवश्यकता है, जबकि compare_strictचार की आवश्यकता है।

आप सोच सकते हैं कि कंपाइलर दूसरे फ़ंक्शन को पसंद कर सकता है:

int compare_loose(double a, double b) { return ! (a > b); }

हालांकि यह गलत तरीके से NaN को हैंडल करेगा। NaN1 <= NaN2और NaN1 > NaN2दोनों को असत्य का मूल्यांकन करने की आवश्यकता है।


सौभाग्य से यह x86 (x87) पर इस तरह काम नहीं करता है। fucomipZF और CF सेट करता है।
जोनाथन रेइनहार्ट 20

3
@JonathonReinhart: मैं आपको लगता हो गलतफहमी क्या PowerPC कर रही है - हालत रजिस्टर cr है की तरह झंडे के बराबर ZFऔर CF86 पर। (हालांकि सीआर अधिक लचीला है।) जिस पोस्टर के बारे में बात की जा रही है वह परिणाम को एक जीपीआर में ले जा रहा है: जो पावरपीसी पर दो निर्देश लेता है, लेकिन x86 में एक सशर्त चाल निर्देश है।
डायट्रिच एप्प

@DietrichEpp मेरे कथन के बाद मुझे जो जोड़ना था, वह था: जिसे आप तुरंत EFLAGS के मूल्य के आधार पर कूद सकते हैं। स्पष्ट नहीं होने के लिए क्षमा करें।
जोनाथन रेनहार्ट

1
@JonathonReinhart: हाँ, और आप सीआर के मूल्य के आधार पर तुरंत कूद भी सकते हैं। जवाब कूदने के बारे में बात नहीं कर रहा है, जो कि अतिरिक्त निर्देश कहां से आया है।
डेट्रिच एप्प

34

हो सकता है कि उस अनाम पुस्तक के लेखक ने पढ़ा हो जो a > 0तेजी से चलता है a >= 1और सोचता है कि यह सार्वभौमिक रूप से सच है।

लेकिन ऐसा इसलिए है क्योंकि 0इसमें शामिल है (क्योंकि CMP, आर्किटेक्चर के आधार पर, उदाहरण के साथ प्रतिस्थापित किया जा सकता है OR) और इसकी वजह से नहीं <


1
यकीन है, एक "डिबग" बिल्ड में, लेकिन यह (a >= 1)धीमी चलाने के लिए एक खराब संकलक लगेगा (a > 0), क्योंकि पूर्व को तुच्छ रूप से ऑप्टिमाइज़र द्वारा उत्तरार्द्ध में बदल दिया जा सकता है ..
BeeOnRope

2
@BeeOnRope कभी-कभी मुझे आश्चर्य होता है कि एक ऑप्टिमाइज़र किन जटिल चीज़ों को ऑप्टिमाइज़ कर सकता है और किन आसान चीज़ों पर ऐसा करने में विफल रहता है।
जगल

1
वास्तव में, और यह हमेशा बहुत कम कार्यों के लिए asm आउटपुट की जांच करने के लायक है जहां यह बात होगी। उस ने कहा, उपरोक्त परिवर्तन बहुत बुनियादी है और दशकों तक सरल संकलक में भी प्रदर्शन किया गया है।
मधुमक्खी पालन

32

बहुत कम से कम, अगर यह सच होता तो एक कंपाइलर एक <= b से (a> b) को तुच्छ रूप से ऑप्टिमाइज़ कर सकता था, और इसलिए भले ही तुलना वास्तव में धीमी हो, लेकिन सभी भोले कंपाइलर के साथ आपको अंतर नजर नहीं आएगा! ।


क्यों! (A> b) <= b का अनुकूलित संस्करण है। एक में 2 ऑपरेशन नहीं है?
अभिषेक सिंह

6
@ अभिषेकसिंह NOTसिर्फ अन्य निर्देश ( jeबनाम jne) से बना है
पावेल गटनार

15

उनकी एक ही गति है। हो सकता है कि कुछ विशेष वास्तुकला में उसने जो कहा, वह सही हो, लेकिन x86 परिवार में कम से कम मुझे पता है कि वे समान हैं। क्योंकि ऐसा करने के लिए सीपीयू एक विकल्प (ए - बी) करेगा और फिर ध्वज रजिस्टर के झंडे की जांच करेगा। उस रजिस्टर के दो बिट्स को ZF (जीरो फ्लैग) और SF (साइन फ्लैग) कहा जाता है, और यह एक चक्र में किया जाता है, क्योंकि यह एक मास्क ऑपरेशन के साथ करेगा।


14

यह अंतर्निहित आर्किटेक्चर पर अत्यधिक निर्भर होगा जो C को संकलित किया गया है। कुछ प्रोसेसर और आर्किटेक्चर के पास अलग-अलग संख्या में चक्रों के निष्पादन के बराबर या उससे कम या उसके बराबर के लिए स्पष्ट निर्देश हो सकते हैं।

हालांकि यह बहुत असामान्य होगा, क्योंकि कंपाइलर इसके चारों ओर काम कर सकता है, जिससे यह अप्रासंगिक हो जाएगा।


1
अगर शैलियों में अंतर था। 1) यह पता लगाने योग्य नहीं होगा। 2) इसके नमक के लायक कोई भी कंपाइलर पहले से ही कोड के अर्थ को बदले बिना स्लो फॉर्म से तेज फॉर्म में बदलाव कर रहा होगा। इसलिए लगाया गया अनुदेश समान होगा।
मार्टिन यॉर्क

पूरी तरह से सहमत, यह किसी भी मामले में एक बहुत तुच्छ और मूर्खतापूर्ण अंतर होगा। निश्चित रूप से एक किताब में उल्लेख करने के लिए कुछ भी नहीं है जो कि मंच अज्ञेय होना चाहिए।
तेलजिन

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

12

टीएल; डीआर जवाब

वास्तुकला, संकलक और भाषा के अधिकांश संयोजनों के लिए यह जल्दी नहीं होगा।

पूरा जवाब

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

जैसे, मैं सुझाव दूंगा कि इस प्रकार का माइक्रो-ऑप्टिमाइज़ेशन सर्वश्रेष्ठ सॉफ्टवेयर इंजीनियरिंग अभ्यास के बजाय कार्गो पंथ प्रोग्रामिंग का एक उदाहरण है ।

संभवतः कुछ आर्किटेक्चर हैं जहां यह एक अनुकूलन है, लेकिन मुझे कम से कम एक आर्किटेक्चर का पता है जहां विपरीत सच हो सकता है। सम्मानित Transputer वास्तुकला के लिए ही मशीन कोड निर्देश था के बराबर और से बड़ा या बराबर है, इसलिए सभी तुलना इन पुरातन से बनाया जाना था।

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

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


6

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


1
क्या बुरा अभ्यास है? एक काउंटर बढ़ाना या घटाना? आप इंडेक्स नोटेशन को कैसे स्टोर करते हैं?
jcolebrand

5
यदि आप 2 चर प्रकारों की तुलना कर रहे हैं तो उसका मतलब है। यदि आप एक लूप या कुछ के लिए मूल्य निर्धारित कर रहे हैं, तो बेशक यह तुच्छ है। लेकिन अगर आपके पास x <= y है, और y अज्ञात है, तो यह x <y + 1 के लिए इसे 'ऑप्टिमाइज़' करने के लिए धीमा होगा
जस्टिनडिनेलसन

@JustinDanielson सहमत हुए। बदसूरत, भ्रामक, आदि का उल्लेख नहीं है
जोनाथन रेनहार्ट

4

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


मैं कुछ हद तक असहमत हूं। प्रतिस्पर्धी प्रोग्रामिंग में, स्क्रिप्टिंग भाषा अक्सर एक समस्या का सबसे तेज समाधान पेश करती है, लेकिन सही समाधान प्राप्त करने के लिए सही तकनीकों (पढ़ें: अनुकूलन) को लागू किया जाना चाहिए।
टायलर क्रॉम्पटन

3

जब मैंने यह उत्तर लिखा था, मैं केवल सामान्य रूप में <बनाम <= के बारे में शीर्षक प्रश्न देख रहा था, न कि एक स्थिर a < 901बनाम का विशिष्ट उदाहरण a <= 900। कई कंपाइलर हमेशा <और के बीच परिवर्तित करके स्थिरांक की भयावहता को कम करते हैं <=, जैसे कि x86 तत्काल ऑपरेंड में -128 के लिए एक छोटा बाइट एन्कोडिंग होता है ।.127।

एआरएम और विशेष रूप से AArch64 के लिए, एक तत्काल के रूप में सांकेतिक शब्दों में बदलना एक संकीर्ण क्षेत्र को एक शब्द में किसी भी स्थिति में घुमाने में सक्षम होने पर निर्भर करता है। अतः cmp w0, #0x00f000अतिक्रमण होगा, जबकि cmp w0, #0x00effffनहीं हो सकता है। इसलिए तुलनात्मक संकलन के लिए मेक-इट-छोटा नियम हमेशा AArch64 के लिए लागू नहीं होता है।


सामान्य रूप से रन-चर स्थितियों के लिए <बनाम <= सामान्य रूप से

अधिकांश मशीनों पर असेंबली भाषा में, एक तुलना के लिए एक <=ही लागत है <। यह उस पर लागू होता है चाहे आप उस पर शाखा लगा रहे हों, इसे 0/1 पूर्णांक बनाने के लिए बूलियनाइज़ कर रहे हों, या इसे एक शाखा रहित चयन ऑपरेशन (जैसे x86 CMOV) के लिए विधेय के रूप में उपयोग कर रहे हों। अन्य उत्तरों ने केवल प्रश्न के इस भाग को संबोधित किया है।

लेकिन यह सवाल C ++ ऑपरेटरों, अनुकूलक के इनपुट के बारे में है । आम तौर पर वे दोनों समान रूप से कुशल होते हैं; पुस्तक से सलाह पूरी तरह से फर्जी लगती है क्योंकि कंपाइलर हमेशा उस तुलना को बदल सकते हैं जिसे वे एसम में लागू करते हैं। लेकिन कम से कम एक अपवाद है जहां <=संयोगवश कुछ ऐसा बना सकते हैं जो संकलक अनुकूलन नहीं कर सकता है।

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

हस्ताक्षरित ओवरफ्लो (यूबी) के विपरीत, बिना सौंपा ओवरफ्लो को बेस -2 रैप के रूप में अच्छी तरह से परिभाषित किया गया है। हस्ताक्षर किए गए लूप काउंटर आमतौर पर संकलक के साथ सुरक्षित होते हैं जो हस्ताक्षरित-अतिप्रवाह यूबी के आधार पर अनुकूलन करते हैं जो नहीं हो रहा है: ++i <= sizeहमेशा अंततः गलत हो जाएगा। ( हर सी प्रोग्रामर को अपरिभाषित व्यवहार के बारे में क्या जानना चाहिए )

void foo(unsigned size) {
    unsigned upper_bound = size - 1;  // or any calculation that could produce UINT_MAX
    for(unsigned i=0 ; i <= upper_bound ; i++)
        ...

कंपाइलर केवल उन तरीकों को अनुकूलित कर सकते हैं जो सभी संभावित इनपुट मानों के लिए C ++ स्रोत के (परिभाषित और कानूनी रूप से अवलोकन योग्य) व्यवहार को संरक्षित करते हैं , सिवाय उन लोगों के जो अपरिभाषित व्यवहार का नेतृत्व करते हैं।

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

इस मामले में, size=0की ओर जाता है upper_bound=UINT_MAX, और i <= UINT_MAXहमेशा सच है। तो यह लूप के लिए अनंत है size=0, और कंपाइलर को सम्मान देना होगा कि भले ही आप प्रोग्रामर के रूप में कभी भी आकार = 0 पास करने का इरादा न करें। यदि कंपाइलर इस फ़ंक्शन को एक कॉलर में इनलाइन कर सकता है, जहां यह साबित कर सकता है कि आकार = 0 असंभव है, तो बहुत अच्छा, यह ऑप्टिमाइज़ कर सकता है जैसे यह कर सकता है i < size

जैसे लूप if(!size) skip the loop; do{...}while(--size);ऑप्टिमाइज़ करने का एक सामान्य तरीका है for( i<size ), अगर लूप के iअंदर वास्तविक मूल्य की आवश्यकता नहीं है ( क्यों लूप हमेशा "डू ... कॉम्प्लेक्स" (टेल जंप)? ) में संकलित किए जाते हैं

लेकिन वह {} जबकि अनंत नहीं हो सकता है: अगर साथ में प्रवेश किया है size==0, तो हमें 2 ^ n पुनरावृत्तियों मिलते हैं। ( लूप C के लिए सभी अहस्ताक्षरित पूर्णांक से अधिक का उपयोग करना) शून्य सहित सभी अहस्ताक्षरित पूर्णांकों पर एक लूप व्यक्त करना संभव बनाता है, लेकिन जिस तरह से यह asm में है, उसे कैरी फ़्लैग के बिना आसान नहीं है।)

लूप काउंटर के रैपराउंड के साथ एक संभावना होने के नाते, आधुनिक कंपाइलर अक्सर बस "छोड़ देते हैं", और लगभग आक्रामक रूप से अनुकूलन नहीं करते हैं।

उदाहरण: 1 से n तक पूर्णांकों का योग

अहस्ताक्षरित i <= nपराजित क्लैंग के मुहावरे-मान्यता का उपयोग करना जोsum(1 .. n) गॉस के n * (n+1) / 2फार्मूले के आधार पर एक बंद फॉर्म के साथ लूप को अनुकूलित करता है।

unsigned sum_1_to_n_finite(unsigned n) {
    unsigned total = 0;
    for (unsigned i = 0 ; i < n+1 ; ++i)
        total += i;
    return total;
}

Godbolt संकलक एक्सप्लोरर पर xang7.0 और gcc8.2 से x86-64 asm

 # clang7.0 -O3 closed-form
    cmp     edi, -1       # n passed in EDI: x86-64 System V calling convention
    je      .LBB1_1       # if (n == UINT_MAX) return 0;  // C++ loop runs 0 times
          # else fall through into the closed-form calc
    mov     ecx, edi         # zero-extend n into RCX
    lea     eax, [rdi - 1]   # n-1
    imul    rax, rcx         # n * (n-1)             # 64-bit
    shr     rax              # n * (n-1) / 2
    add     eax, edi         # n + (stuff / 2) = n * (n+1) / 2   # truncated to 32-bit
    ret          # computed without possible overflow of the product before right shifting
.LBB1_1:
    xor     eax, eax
    ret

लेकिन भोले संस्करण के लिए, हमें बस क्लैंग से एक गूंगा लूप मिलता है।

unsigned sum_1_to_n_naive(unsigned n) {
    unsigned total = 0;
    for (unsigned i = 0 ; i<=n ; ++i)
        total += i;
    return total;
}
# clang7.0 -O3
sum_1_to_n(unsigned int):
    xor     ecx, ecx           # i = 0
    xor     eax, eax           # retval = 0
.LBB0_1:                       # do {
    add     eax, ecx             # retval += i
    add     ecx, 1               # ++1
    cmp     ecx, edi
    jbe     .LBB0_1            # } while( i<n );
    ret

जीसीसी किसी भी तरह से एक बंद-फॉर्म का उपयोग नहीं करता है, इसलिए लूप स्थिति का विकल्प वास्तव में इसे चोट नहीं पहुंचाता है ; यह SIMD पूर्णांक जोड़ के साथ ऑटो-वेक्टर करता है, जो iएक एक्सएमएम रजिस्टर के तत्वों में समानांतर में 4 मान चलाता है ।

# "naive" inner loop
.L3:
    add     eax, 1       # do {
    paddd   xmm0, xmm1    # vect_total_4.6, vect_vec_iv_.5
    paddd   xmm1, xmm2    # vect_vec_iv_.5, tmp114
    cmp     edx, eax      # bnd.1, ivtmp.14     # bound and induction-variable tmp, I think.
    ja      .L3 #,       # }while( n > i )

 "finite" inner loop
  # before the loop:
  # xmm0 = 0 = totals
  # xmm1 = {0,1,2,3} = i
  # xmm2 = set1_epi32(4)
 .L13:                # do {
    add     eax, 1       # i++
    paddd   xmm0, xmm1    # total[0..3] += i[0..3]
    paddd   xmm1, xmm2    # i[0..3] += 4
    cmp     eax, edx
    jne     .L13      # }while( i != upper_limit );

     then horizontal sum xmm0
     and peeled cleanup for the last n%3 iterations, or something.

इसमें एक प्लेन स्केलर लूप भी है जो मुझे लगता है कि यह बहुत छोटे nऔर / या अनंत लूप केस के लिए उपयोग करता है ।

BTW, इन दोनों छोरों लूप ओवरहेड पर एक निर्देश (और सैंडब्रिज-परिवार सीपीयू पर एक यूओपी) बर्बाद करते हैं। sub eax,1/ cmp / jcc के jnzबजाय add eax,1अधिक कुशल होगा। 2 के बजाय 1 यूओपी (उप / jcc या cmp / jcc के मैक्रो-फ्यूजन के बाद)। दोनों लूप के बाद का कोड EAX बिना शर्त लिखता है, इसलिए यह लूप काउंटर के अंतिम मूल्य का उपयोग नहीं कर रहा है।


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

@rustyx: मैंने टिप्पणी की कि कहीं और एक उत्तर के तहत? कंपाइलर्स कोड का उत्सर्जन नहीं करते हैं जो आंशिक-फ्लैग स्टॉल का कारण बनता है, और निश्चित रूप से सी <या के लिए नहीं <=। लेकिन यकीन है कि, test ecx,ecx/ bt eax, 3/ jbeयदि जेडएफ, (ECX == 0) सेट कर दिया जाता है या यदि सीएफ सेट किया गया है (थोड़ा EAX के 3 == 1) पर चला जाएगा, सबसे CPUs पर एक आंशिक झंडा स्टाल के कारण क्योंकि झंडे इसे पढ़ता है सब नहीं है किसी भी झंडे को लिखने के लिए अंतिम निर्देश से आते हैं। सैंडीब्रिज-परिवार में, यह वास्तव में स्टाल नहीं है, बस एक मर्जिंग यूओपी डालने की जरूरत है। cmp/ testसभी झंडे लिखें, लेकिन btZF को असंबद्ध छोड़ देता है। felixcloutier.com/x86/bt
पीटर कॉर्ड्स

2

केवल अगर कंप्यूटर बनाने वाले लोग बूलियन लॉजिक के साथ खराब हैं। जो उन्हें नहीं होना चाहिए।

हर तुलना ( >= <= > <) एक ही गति से की जा सकती है।

हर तुलना जो है, वह केवल एक घटाव (अंतर) है और यह देखने पर कि यह सकारात्मक / नकारात्मक है।
(यदि msbसेट किया गया है, तो संख्या ऋणात्मक है)

कैसे करें जांच a >= b? सब पॉजिटिव होने पर a-b >= 0चेक करें a-b
कैसे करें जांच a <= b? सब पॉजिटिव होने पर 0 <= b-aचेक करें b-a
कैसे करें जांच a < b? a-b < 0यदि a-bनकारात्मक है तो सब चेक करें ।
कैसे करें जांच a > b? 0 > b-aयदि b-aनकारात्मक है तो सब चेक करें ।

सीधे शब्दों में कहें, कंप्यूटर बस दिए गए ऑप के लिए हुड के नीचे ऐसा कर सकता है:

a >= b== msb(a-b)==0
a <= b== msb(b-a)==0
a > b== msb(b-a)==1
a < b==msb(a-b)==1

और निश्चित रूप से कंप्यूटर को वास्तव में ==0या ==1तो करने की आवश्यकता नहीं होगी ।
के लिए ==0यह सिर्फ msbसर्किट से पलटना कर सकता है ।

वैसे भी, वे निश्चित a >= bरूप से a>b || a==bयोग्य के रूप में गणना नहीं की गई होगी

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