अगर स्टेटमेंट बनाम अगर-और स्टेटमेंट है, जो तेज है? [बन्द है]


84

मैंने दूसरे दिन एक दोस्त के साथ उन दो स्निपेट के बारे में बहस की। कौन सा तेज है और क्यों?

value = 5;
if (condition) {
    value = 6;
}

तथा:

if (condition) {
    value = 6;
} else {
    value = 5;
}

यदि valueएक मैट्रिक्स है तो क्या होगा

नोट: मैं जानता हूं कि यह value = condition ? 6 : 5;मौजूद है और मैं इसके तेज होने की उम्मीद करता हूं, लेकिन यह एक विकल्प नहीं था।

संपादित करें (प्रश्न के समय से कर्मचारियों द्वारा अनुरोधित):

  • कृपया अनुकूलित और गैर-अनुकूलित संस्करणों या MIPS असेंबली दोनों में मुख्यधारा कंपाइलर ( जैसे g ++, clang ++, vc, mingw ) द्वारा उत्पन्न x86 असेंबली पर विचार करके उत्तर दें ।
  • जब असेंबली में अंतर होता है, तो बताएं कि कोई संस्करण क्यों तेज है और कब ( उदाहरण के लिए "बेहतर है क्योंकि कोई ब्रांचिंग और ब्रांचिंग के बाद समस्या ब्लाहब्लह" नहीं है )

173
अनुकूलन सभी को मार देगा ... इससे कोई फर्क नहीं पड़ता ...
क्वांटम भौतिक विज्ञानी

21
आप इसे प्रोफाइल कर सकते हैं, व्यक्तिगत रूप से मुझे संदेह है कि आप आधुनिक संकलक का उपयोग करके कोई अंतर देखेंगे।
जॉर्ज

25
के value = condition ? 6 : 5;बजाय का उपयोग कर if/elseसबसे अधिक संभावना एक ही कोड उत्पन्न होने का परिणाम होगा। यदि आप अधिक जानना चाहते हैं तो विधानसभा आउटपुट देखें।
Jabberwocky

8
इस मामले में सबसे महत्वपूर्ण बात शाखा से बचना है, जो यहां सबसे महंगी चीज है। (पाइप पुनः लोड, पूर्व-प्राप्त निर्देशों को छोड़ दें आदि)
Tommylee2k

11
एक ही समय में यह गति के लिए माइक्रो-ऑप्टिमाइज़ेशन के लिए किसी भी तरह से समझ में आता है जैसे कि एक लूप के अंदर होता है जो कई बार चलेगा, और या तो ऑप्टिमाइज़र सभी शाखा निर्देशों को ऑप्टिमाइज़ कर सकता है, जैसा कि इस तुच्छ उदाहरण या वास्तविक दुनिया के लिए gcc कर सकता है। प्रदर्शन सही शाखा भविष्यवाणी ( stackoverflow.com/questions/11227809/… के लिए अनिवार्य लिंक ) पर बहुत अधिक निर्भर करेगा । यदि आप अनपेक्षित रूप से एक लूप के अंदर शाखा करते हैं, तो आप एक प्रोफ़ाइल बनाकर और उसके साथ फिर से जुड़कर शाखा के भविष्यवक्ता की मदद कर सकते हैं।
डेविसलर

जवाबों:


282

टीएल; डीआर:if बिना अडॉप्ट किए हुए कोड में, elseअप्रासंगिक रूप से अधिक कुशल लगता है , लेकिन अनुकूलन के सबसे बुनियादी स्तर के साथ भी कोड को मूल रूप से फिर से लिखा जाता है value = condition + 5


मैंने इसे एक कोशिश दी और निम्नलिखित कोड के लिए विधानसभा उत्पन्न की:

int ifonly(bool condition, int value)
{
    value = 5;
    if (condition) {
        value = 6;
    }
    return value;
}

int ifelse(bool condition, int value)
{
    if (condition) {
        value = 6;
    } else {
        value = 5;
    }
    return value;
}

विकलांगों के साथ gcc 6.3 पर विकलांग ( -O0), प्रासंगिक अंतर है:

 mov     DWORD PTR [rbp-8], 5
 cmp     BYTE PTR [rbp-4], 0
 je      .L2
 mov     DWORD PTR [rbp-8], 6
.L2:
 mov     eax, DWORD PTR [rbp-8]

के लिए ifonly, जबकि ifelseहै

 cmp     BYTE PTR [rbp-4], 0
 je      .L5
 mov     DWORD PTR [rbp-8], 6
 jmp     .L6
.L5:
 mov     DWORD PTR [rbp-8], 5
.L6:
 mov     eax, DWORD PTR [rbp-8]

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

हालांकि, यहां तक ​​कि सबसे कम अनुकूलन स्तर ( -O1) के साथ दोनों कार्य समान हो जाते हैं:

test    dil, dil
setne   al
movzx   eax, al
add     eax, 5

जो मूल रूप से के बराबर है

return 5 + condition;

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


डिस्क्लेमर: आपको शायद 5 + conditionखुद नहीं लिखना चाहिए (भले ही मानक गारंटी जो trueपूर्णांक प्रकार को परिवर्तित करने के लिए देता है 1), क्योंकि आपका इरादा आपके कोड को पढ़ने वाले लोगों को तुरंत स्पष्ट नहीं हो सकता है (जिसमें आपका भविष्य स्वयं शामिल हो सकता है)। इस कोड का बिंदु यह दिखाना है कि संकलक दोनों मामलों में क्या उत्पन्न करता है (व्यावहारिक रूप से) समान है। Ciprian Tomoiaga ने टिप्पणी में इसे बहुत अच्छी तरह से बताया:

एक मानव के काम कोड लिखने के लिए मनुष्य के लिए दो और संकलक के लिए लिखने कोड मशीन


50
यह एक शानदार उत्तर है और इसे स्वीकार किया जाना चाहिए।
dtell

10
मैं इसके अलावा का उपयोग करने के बावजूद कभी नहीं होता (<- क्या अजगर आप के लिए करता है।)
Ciprian Tomoiagă

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

22
मैं मुज़ेर को जवाब देना चाहता था, लेकिन इसने कुछ भी नहीं जोड़ा। हालाँकि, मैं केवल यह बताना चाहता हूं कि मनुष्य का काम मनुष्यों के लिए कोड लिखना और मशीन के लिए कंपाइलर कोड लिखना है । मैंने कहा कि एक कंपाइलर डेवलपर PoV से (जो कि मैं नहीं हूं, लेकिन मैंने उनके बारे में थोड़ा सा जान लिया)
Ciprian Tomoiagă

10
मान trueको intहमेशा 1, अवधि में परिवर्तित किया जाता है । बेशक, यदि आपकी स्थिति केवल "सत्य" है और boolमूल्य नहीं है true, तो यह पूरी तरह से अलग मामला है।
TC

44

CompuChip के उत्तर से पता चलता है कि intवे दोनों एक ही विधानसभा के लिए अनुकूलित हैं, इसलिए इससे कोई फर्क नहीं पड़ता।

क्या होगा अगर मूल्य एक मैट्रिक्स है?

मैं इसे और अधिक सामान्य तरीके से व्याख्या करूंगा, अर्थात क्या होगा यदि valueएक प्रकार का है जिसके निर्माण और असाइनमेंट महंगे हैं (और चालें सस्ती हैं)।

फिर

T value = init1;
if (condition)
   value = init2;

यदि conditionयह सत्य है, तो आप उप-इष्टतम हैं , आप init1प्रतिलिपि को असाइन करने के लिए अनावश्यक प्रारंभ करते हैं।

T value;
if (condition)
   value = init2;
else
   value = init3;

यह बेहतर है। लेकिन फिर भी उप-इष्टतम यदि डिफ़ॉल्ट निर्माण महंगा है और यदि प्रतिलिपि निर्माण अधिक महंगा है तो आरंभीकरण।

आपके पास सशर्त ऑपरेटर समाधान है जो अच्छा है:

T value = condition ? init1 : init2;

या, यदि आप सशर्त ऑपरेटर को पसंद नहीं करते हैं, तो आप इस तरह एक सहायक कार्य कर सकते हैं:

T create(bool condition)
{
  if (condition)
     return {init1};
  else
     return {init2};
}

T value = create(condition);

क्या पर निर्भर करता है init1और init2आप भी इस पर विचार कर सकते हैं:

auto final_init = condition ? init1 : init2;
T value = final_init;

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


3
महंगा और बाहर अनुकूलित नहीं। उदाहरण के लिए, यदि डिफॉल्ट कंस्ट्रक्टर मैट्रिक्स को शून्य-आउट करता है, तो कंपाइलर यह महसूस कर सकता है कि असाइनमेंट उन 0s को ओवरराइट करने के बारे में है, इसलिए इसे बिल्कुल भी शून्य न करें, और सीधे इस मेमोरी में लिखें। बेशक, ऑप्टिमाइज़र बारीक जानवर हैं, इसलिए यह भविष्यवाणी करना मुश्किल है कि वे किक करते हैं या नहीं ...
Matthieu M.

@MatthieuM। बेशक। कम्पाइलर ऑप्टिमाइज़ेशन के बाद भी "महंगी" का मेरा मतलब "निष्पादित करने के लिए महंगा है (एक मीट्रिक द्वारा, यह सीपीयू घड़ियाँ, संसाधन उपयोग आदि) हो सकता है।
bolov

मुझे यह प्रतीत नहीं होता है कि डिफ़ॉल्ट निर्माण महंगा होगा, लेकिन यह सस्ता है।
प्लगव्यू

6
@plugwash एक बहुत बड़े आवंटित सरणी के साथ एक वर्ग पर विचार करें। डिफ़ॉल्ट कंस्ट्रक्टर सरणी को आवंटित और आरंभ करता है, जो महंगा है। चाल (कॉपी नहीं!) कंस्ट्रक्टर केवल स्रोत ऑब्जेक्ट के साथ संकेत स्वैप कर सकता है और बड़े सरणी को आवंटित या आरंभ करने की आवश्यकता नहीं है।
ट्रेंटपी

1
जब तक भाग सरल होते हैं, मैं निश्चित रूप ?:से एक नया फ़ंक्शन शुरू करने पर ऑपरेटर का उपयोग करना पसंद करूंगा । सब के बाद, आप संभवतः फ़ंक्शन को न केवल शर्त पास करेंगे, बल्कि कुछ निर्माता तर्क भी देंगे। जिनमें से कुछ का उपयोग create()शर्त के आधार पर भी नहीं किया जा सकता है ।
विस्फ़ोटक -

12

छद्म विधानसभा भाषा में,

    li    #0, r0
    test  r1
    beq   L1
    li    #1, r0
L1:

से तेज हो सकता है या नहीं भी

    test  r1
    beq   L1
    li    #1, r0
    bra   L2
L1:
    li    #0, r0
L2:

वास्तविक सीपीयू कितना परिष्कृत है, इस पर निर्भर करता है। सबसे सरल से कट्टरता में जा रहे हैं:

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

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

  • कुछ हद तक अधिक परिष्कृत सीपीयू-फैंसी " आउट-ऑफ-ऑर्डर निष्पादन " करने के लिए पर्याप्त है, लेकिन उस अवधारणा के सर्वोत्तम ज्ञात कार्यान्वयन का उपयोग करने के लिए पर्याप्त फैंसी नहीं है - जब भी वे लिखने के बाद के खतरों का सामना करते हैं, तो पाइपलाइन बुलबुले को उकसा सकते हैं । यह दूसरे उदाहरण के पक्ष में है , जहां r0केवल एक बार लिखा जाता है , चाहे जो भी हो। ये सीपीयू आमतौर पर इंस्ट्रक्शन फ़िशर में बिना शर्त शाखाओं को संसाधित करने के लिए पर्याप्त रूप से फैंसी होते हैं, इसलिए आप ब्रांच पेनल्टी के लिए राइट-आफ्टर राइट पेनल्टी का व्यापार नहीं करते हैं

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

  • सबसे अंत में, OOO इंजन दोनों कोड अंशों के लिए आंतरिक संचालन के बिल्कुल समान अनुक्रम को जारी करेगा - यह "इसके बारे में चिंता न करें, कंपाइलर एक ही तरह से मशीन कोड उत्पन्न करेगा।" हालाँकि, कोड आकार अभी भी मायने रखता है, और अब आपको सशर्त शाखा की भविष्यवाणी के बारे में भी चिंता होनी चाहिए। शाखा पूर्वानुमान विफलताएं संभावित रूप से एक पूर्ण पाइपलाइन फ्लश का कारण बनती हैं , जो प्रदर्शन के लिए विनाशकारी है; देखें कि अनसोल्ड सरणी की तुलना में सॉर्ट किए गए सरणी को संसाधित करना अधिक तेज़ क्यों है? यह समझने के लिए कि यह कितना अंतर कर सकता है।

    यदि शाखा है अत्यधिक अप्रत्याशित, और अपने CPU सशर्त सेट या सशर्त-कदम निर्देश है, इस बार उन्हें इस्तेमाल करने के लिए है:

        li    #0, r0
        test  r1
        setne r0
    

    या

        li    #0, r0
        li    #1, r2
        test  r1
        movne r2, r0
    

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


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

@supercat अंत में पैराग्राफ उस मामले को कवर करने के लिए है, लेकिन मैं इस बारे में सोचूंगा कि इसे कैसे स्पष्ट किया जाए।
zwol

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

यह बहुत ही
सुखद है

9

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

हमेशा की तरह, यदि आप संबंधित हैं, तो असेंबली बनाएं और देखें कि कंपाइलर वास्तव में क्या कर रहा है।


1
यदि प्रदर्शन के बारे में चिंतित हैं, तो वे अभिन्न संकलन नहीं करेंगे। लेकिन निश्चित रूप से कैसे "अच्छा" ऑप्टिमाइज़र संकलक / संस्करण पर निर्भर करता है।
old_timer

AFAIK पर कोई टिप्पणी नहीं है कि कौन से कंपाइलर / सीपीयू आर्किटेक्चर आदि हैं, इसलिए संभावित रूप से, उनके कंपाइलर अनुकूलन नहीं करते हैं। वे 8-बिट PIC से 64-बिट Xeon के लिए कुछ भी संकलित कर सकते हैं।
नील

8

क्या आपको लगता है कि उनमें से किसी एक भी लाइनर तेज या धीमी है?

unsigned int fun0 ( unsigned int condition, unsigned int value )
{
    value = 5;
    if (condition) {
        value = 6;
    }
    return(value);
}
unsigned int fun1 ( unsigned int condition, unsigned int value )
{

    if (condition) {
        value = 6;
    } else {
        value = 5;
    }
    return(value);
}
unsigned int fun2 ( unsigned int condition, unsigned int value )
{
    value = condition ? 6 : 5;
    return(value);
}

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

00000000 <fun0>:
   0:   e3500000    cmp r0, #0
   4:   03a00005    moveq   r0, #5
   8:   13a00006    movne   r0, #6
   c:   e12fff1e    bx  lr

00000010 <fun1>:
  10:   e3500000    cmp r0, #0
  14:   13a00006    movne   r0, #6
  18:   03a00005    moveq   r0, #5
  1c:   e12fff1e    bx  lr

00000020 <fun2>:
  20:   e3500000    cmp r0, #0
  24:   13a00006    movne   r0, #6
  28:   03a00005    moveq   r0, #5
  2c:   e12fff1e    bx  lr

एक बड़ा आश्चर्य नहीं है कि यह एक अलग क्रम में पहला कार्य किया, एक ही निष्पादन समय हालांकि।

0000000000000000 <fun0>:
   0:   7100001f    cmp w0, #0x0
   4:   1a9f07e0    cset    w0, ne
   8:   11001400    add w0, w0, #0x5
   c:   d65f03c0    ret

0000000000000010 <fun1>:
  10:   7100001f    cmp w0, #0x0
  14:   1a9f07e0    cset    w0, ne
  18:   11001400    add w0, w0, #0x5
  1c:   d65f03c0    ret

0000000000000020 <fun2>:
  20:   7100001f    cmp w0, #0x0
  24:   1a9f07e0    cset    w0, ne
  28:   11001400    add w0, w0, #0x5
  2c:   d65f03c0    ret

उम्मीद है कि आपको यह अंदाजा हो सकता है कि आपने अभी यह कोशिश की होगी कि यह स्पष्ट न हो कि विभिन्न कार्यान्वयन वास्तव में अलग नहीं थे।

जहां तक ​​मैट्रिक्स जाता है, यकीन नहीं होता कि कैसे मायने रखता है,

if(condition)
{
 big blob of code a
}
else
{
 big blob of code b
}

कोड के बड़े ब्लब्स के चारों ओर एक ही अगर-फिर-अन्यथा आवरण डालने जा रहे हैं तो उनका मूल्य = 5 या कुछ अधिक जटिल हो सकता है। इसी तरह से तुलना करें, भले ही यह कोड की एक बड़ी बूँद है, जिसे अभी भी गणना करना है, और इसके बराबर या कुछ के बराबर नहीं है अक्सर नकारात्मक के साथ संकलित किया जाता है, अगर (स्थिति) कुछ ऐसा अक्सर संकलित किया जाता है जैसे कि हालत गोटो नहीं।

00000000 <fun0>:
   0:   0f 93           tst r15     
   2:   03 24           jz  $+8         ;abs 0xa
   4:   3f 40 06 00     mov #6, r15 ;#0x0006
   8:   30 41           ret         
   a:   3f 40 05 00     mov #5, r15 ;#0x0005
   e:   30 41           ret         

00000010 <fun1>:
  10:   0f 93           tst r15     
  12:   03 20           jnz $+8         ;abs 0x1a
  14:   3f 40 05 00     mov #5, r15 ;#0x0005
  18:   30 41           ret         
  1a:   3f 40 06 00     mov #6, r15 ;#0x0006
  1e:   30 41           ret         

00000020 <fun2>:
  20:   0f 93           tst r15     
  22:   03 20           jnz $+8         ;abs 0x2a
  24:   3f 40 05 00     mov #5, r15 ;#0x0005
  28:   30 41           ret         
  2a:   3f 40 06 00     mov #6, r15 ;#0x0006
  2e:   30 41

हम अभी हाल ही में स्टैकओवरफ्लो पर किसी और के साथ इस अभ्यास से गुजरे। इस मामले में यह mips संकलक दिलचस्प है कि न केवल एहसास ही कार्य समान थे, लेकिन एक फ़ंक्शन कोड स्थान को बचाने के लिए बस दूसरे पर कूद गया था। हालांकि यहाँ ऐसा नहीं किया

00000000 <fun0>:
   0:   0004102b    sltu    $2,$0,$4
   4:   03e00008    jr  $31
   8:   24420005    addiu   $2,$2,5

0000000c <fun1>:
   c:   0004102b    sltu    $2,$0,$4
  10:   03e00008    jr  $31
  14:   24420005    addiu   $2,$2,5

00000018 <fun2>:
  18:   0004102b    sltu    $2,$0,$4
  1c:   03e00008    jr  $31
  20:   24420005    addiu   $2,$2,5

कुछ और लक्ष्य।

00000000 <_fun0>:
   0:   1166            mov r5, -(sp)
   2:   1185            mov sp, r5
   4:   0bf5 0004       tst 4(r5)
   8:   0304            beq 12 <_fun0+0x12>
   a:   15c0 0006       mov $6, r0
   e:   1585            mov (sp)+, r5
  10:   0087            rts pc
  12:   15c0 0005       mov $5, r0
  16:   1585            mov (sp)+, r5
  18:   0087            rts pc

0000001a <_fun1>:
  1a:   1166            mov r5, -(sp)
  1c:   1185            mov sp, r5
  1e:   0bf5 0004       tst 4(r5)
  22:   0204            bne 2c <_fun1+0x12>
  24:   15c0 0005       mov $5, r0
  28:   1585            mov (sp)+, r5
  2a:   0087            rts pc
  2c:   15c0 0006       mov $6, r0
  30:   1585            mov (sp)+, r5
  32:   0087            rts pc

00000034 <_fun2>:
  34:   1166            mov r5, -(sp)
  36:   1185            mov sp, r5
  38:   0bf5 0004       tst 4(r5)
  3c:   0204            bne 46 <_fun2+0x12>
  3e:   15c0 0005       mov $5, r0
  42:   1585            mov (sp)+, r5
  44:   0087            rts pc
  46:   15c0 0006       mov $6, r0
  4a:   1585            mov (sp)+, r5
  4c:   0087            rts pc

00000000 <fun0>:
   0:   00a03533            snez    x10,x10
   4:   0515                    addi    x10,x10,5
   6:   8082                    ret

00000008 <fun1>:
   8:   00a03533            snez    x10,x10
   c:   0515                    addi    x10,x10,5
   e:   8082                    ret

00000010 <fun2>:
  10:   00a03533            snez    x10,x10
  14:   0515                    addi    x10,x10,5
  16:   8082                    ret

और संकलक

इस i कोड के साथ एक ही मैच के लिए विभिन्न लक्ष्यों की उम्मीद होगी

define i32 @fun0(i32 %condition, i32 %value) #0 {
  %1 = icmp ne i32 %condition, 0
  %. = select i1 %1, i32 6, i32 5
  ret i32 %.
}

; Function Attrs: norecurse nounwind readnone
define i32 @fun1(i32 %condition, i32 %value) #0 {
  %1 = icmp eq i32 %condition, 0
  %. = select i1 %1, i32 5, i32 6
  ret i32 %.
}

; Function Attrs: norecurse nounwind readnone
define i32 @fun2(i32 %condition, i32 %value) #0 {
  %1 = icmp ne i32 %condition, 0
  %2 = select i1 %1, i32 6, i32 5
  ret i32 %2
}


00000000 <fun0>:
   0:   e3a01005    mov r1, #5
   4:   e3500000    cmp r0, #0
   8:   13a01006    movne   r1, #6
   c:   e1a00001    mov r0, r1
  10:   e12fff1e    bx  lr

00000014 <fun1>:
  14:   e3a01006    mov r1, #6
  18:   e3500000    cmp r0, #0
  1c:   03a01005    moveq   r1, #5
  20:   e1a00001    mov r0, r1
  24:   e12fff1e    bx  lr

00000028 <fun2>:
  28:   e3a01005    mov r1, #5
  2c:   e3500000    cmp r0, #0
  30:   13a01006    movne   r1, #6
  34:   e1a00001    mov r0, r1
  38:   e12fff1e    bx  lr


fun0:
    push.w  r4
    mov.w   r1, r4
    mov.w   r15, r12
    mov.w   #6, r15
    cmp.w   #0, r12
    jne .LBB0_2
    mov.w   #5, r15
.LBB0_2:
    pop.w   r4
    ret

fun1:
    push.w  r4
    mov.w   r1, r4
    mov.w   r15, r12
    mov.w   #5, r15
    cmp.w   #0, r12
    jeq .LBB1_2
    mov.w   #6, r15
.LBB1_2:
    pop.w   r4
    ret


fun2:
    push.w  r4
    mov.w   r1, r4
    mov.w   r15, r12
    mov.w   #6, r15
    cmp.w   #0, r12
    jne .LBB2_2
    mov.w   #5, r15
.LBB2_2:
    pop.w   r4
    ret

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


0

ठीक है, चूंकि विधानसभा टैग में से एक है, मैं मान लूंगा कि आपका कोड छद्म कोड है (और जरूरी नहीं कि c) और इसे मानव द्वारा 6502 विधानसभा में अनुवादित किया जाए।

पहला विकल्प (बिना अन्य)

        ldy #$00
        lda #$05
        dey
        bmi false
        lda #$06
false   brk

दूसरा विकल्प (अन्य के साथ)

        ldy #$00
        dey
        bmi else
        lda #$06
        sec
        bcs end
else    lda #$05
end     brk

मान: शर्त Y रजिस्टर में है, इसे या तो विकल्प की पहली पंक्ति पर 0 या 1 पर सेट करें, परिणाम संचयकर्ता में होगा।

इसलिए, प्रत्येक मामले की दोनों संभावनाओं के लिए चक्रों की गणना करने के बाद, हम देखते हैं कि 1 निर्माण आम तौर पर तेज है; जब हालत 1 होती है तो 9 चक्र और जब स्थिति 1 होती है तो 10 चक्र होते हैं, जबकि विकल्प दो भी 9 चक्र होते हैं जब स्थिति 0 होती है, लेकिन स्थिति 1 होने पर 13 चक्र होते हैं ( चक्र गणना BRKअंत में शामिल नहीं होती है )।

निष्कर्ष: निर्माण की If onlyतुलना में तेज है If-Else

और पूर्णता के लिए, यहाँ एक अनुकूलित value = condition + 5समाधान है:

ldy #$00
lda #$00
tya
adc #$05
brk

इससे हमारा समय 8 चक्रों तक कम हो जाता है ( फिर BRKअंत में शामिल नहीं होता है )।


6
इस उत्तर के लिए दुर्भाग्य से, एक ही स्रोत कोड को C कंपाइलर (या C ++ कंपाइलर में) को खिलाने से ग्लेन के मस्तिष्क में इसे खिलाने की तुलना में बहुत अधिक उत्पादन होता है। स्रोत कोड स्तर पर किसी भी विकल्प के बीच कोई अंतर, कोई "अनुकूलन" क्षमता नहीं है। बस एक का उपयोग करें जो सबसे अधिक पठनीय है (संभवत: यदि / अन्यथा एक)।
क्क्सप्लसोन

1
@हां। कंपाइलर या तो सबसे तेज़ संस्करण में दोनों वेरिएंट को ऑप्टिमाइज़ करने की संभावना रखता है , या अतिरिक्त ओवरहेड जोड़ने की संभावना है जो दोनों के बीच अंतर को दूर करता है। अथवा दोनों।
jpaugh

1
यह मानते हुए कि यह " जरूरी नहीं है कि सी " एक समझदार विकल्प लगता है, यह देखते हुए कि प्रश्न को सी ++ के रूप में टैग किया गया है (दुर्भाग्य से, इसमें शामिल प्रकारों को घोषित करने का प्रबंधन नहीं है )।
टोबी स्पाइट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.