c++ - मुझे दावे क्यों करना चाहिए?




performance assertions (15)

मुझे कभी भी इस बात का विचार नहीं आया - आप कभी भी उनका उपयोग क्यों करें?

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

परीक्षण (डिबग में) सभी ठीक थे, लेकिन अब हम रेसिंग (रिलीज़) करना चाहते हैं! क्या हमें सभी सुरक्षा को छोड़ देना चाहिए, क्योंकि परीक्षण के दौरान कोई समस्या नहीं थी?

मैं उन्हें कभी नहीं हटाऊंगा। मुझे लगता है कि दावा करने वाले अधिकांश लोग यह दावा करते हैं कि मुखर करने के लिए तुलनीय चीज को हटाने से उनके कोड को कभी भी अप्रूव्ड नहीं किया गया या मुखर पूर्ण विस्थापित नहीं हुए। मैंने विशेष रूप से 80/20 नियम के संबंध में कोई वास्तविक प्रदर्शन लाभ नहीं देखा है।

तो, क्या मुझे किसी तरह की बात याद आ रही है, या कोई मुझे बता सकता है, मुझे क्यों जोर देना चाहिए? वैसे, मैं यूनिट परीक्षणों का उपयोग कर रहा हूं।


Answers

कोड में पूर्ण एक ऐसा खंड है जो कुछ ऐसा कहता है। हर बार जब आप लिखते हैं तो बिना किसी और चीज़ के आपको कुछ याद आ रहा है।

यह इस कोड की तरह है

int i = 1
i = i++ 

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

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

मैं आपसे सहमत हूं कि उत्पादन कोड में अक्षम दावे, बल्कि बेकार कर देते हैं। और जावा vm के मामले में डिफ़ॉल्ट दावे से मेरी राय में खतरा है।


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

इससे भी महत्वपूर्ण बात, मैंने कोड लिखा है जहाँ अभिकथनों ने कोड के आकार के लिए दसियों प्रतिशत अंक का अंतर किया। जब स्मृति पदचिह्न एक मुद्दा है, तो रिलीज कोड में जोर शायद एक अस्वीकार्य अपव्यय है।


मैंने जिस परियोजना में काम किया है, उसमें कई डीबग और रिलीज़ में अलग-अलग व्यवहार करने वाले कस्टम मैक्रो के साथ दावे किए गए थे।

डिबग में, यदि स्थिति झूठी है, तो कोड में उस बिंदु पर डिबगर चालू हो जाता है।

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


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

बाकी सब के लिए, एक अपवाद फेंक दें।


क्योंकि वे डिबगिंग को आसान बनाते हैं।

डिबगिंग का समय लेने वाला हिस्सा उस लक्षण से एक समस्या का पता लगा रहा है जिसे आप पहली बार कोड में त्रुटि पर ध्यान देते हैं। अच्छी तरह से लिखी गई बातें आपके द्वारा वास्तविक कोड समस्या के बहुत करीब आने वाले लक्षण को स्पष्ट कर देंगी।

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


मैं इसे ज्यादातर विकास के दौरान परीक्षणों के लिए उपयोग करता हूं। उदाहरण के लिए, यहां मेरी utf-8 लाइब्रेरी का स्मोक टेस्ट है। जब भी मैं लाइब्रेरी कोड में बदलाव करता हूं, तो मैं परीक्षण चलाता हूं और यदि बग को पेश किया जाता है तो एक ट्रिगर होगा। बेशक, मैं एक पूर्ण विकसित इकाई परीक्षण ढांचे का उपयोग कर सकता था, लेकिन मेरे उद्देश्यों के लिए सिर्फ ठीक हैं।


यह एक विवादास्पद विषय है। कई लोग, खुद की तरह, वास्तव में उन्हें उत्पादन कोड में छोड़ना पसंद करते हैं। यदि आपका कार्यक्रम वैसे भी मातम में जा रहा है, तो आपके पास इसमें जोर भी हो सकता है, ताकि आपका ग्राहक कम से कम आपको लाइन नंबर और फ़ाइल नाम दे सके (या जो भी जानकारी या क्रिया आप करने के लिए मुखर को कॉन्फ़िगर करते हैं)। यदि आपने दावे को छोड़ दिया, तो सभी ग्राहक आपको रिपोर्ट कर सकते हैं कि "यह दुर्घटनाग्रस्त हो गया"।

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


सबसे पहले, प्रदर्शन अंतर बहुत बड़ा हो सकता है। एक परियोजना में हमारे जोर-जोर से एक 3x मंदी का कारण बना। लेकिन उन्होंने हमें कुछ वास्तव में अजीब बग को उजागर करने में मदद की।

जो बिल्कुल बिंदु है।

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

यहां तक ​​कि त्रुटि को पकड़ना और अपवाद को फेंकना वास्तव में समाधान नहीं है। कार्यक्रम का तर्क त्रुटिपूर्ण है, और भले ही हम अपवाद को संभाल लें, लेकिन कार्यक्रम अभी भी टूटा हुआ है।

मूल रूप से जो उबलता है, वह यह है कि "परेशान करने वाली त्रुटियों को क्यों आप संभाल नहीं सकते?"

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

मुझे कभी भी इस बात का विचार नहीं आया - आप कभी भी उनका उपयोग क्यों करें?

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

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

लेकिन जांच के बारे में क्या देखना है कि इंजन स्थापित है? क्या हमें दौड़ के दौरान जांचने की आवश्यकता है?

बिलकूल नही। यदि हम बिना इंजन के दौड़ में शामिल हो जाते हैं, तो हम घबरा जाते हैं, और यदि हम त्रुटि का पता लगाते हैं, तो भी इसके बारे में कुछ भी करने में देर हो जाती है।

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

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

एक मुखर आपकी मदद करने के लिए है , आपको त्रुटियों के लिए चेतावनी देकर जो पहले कभी नहीं होनी चाहिए, उत्पाद को भेजने से पहले इसे ठीक किया जाना चाहिए। त्रुटियां जो उपयोगकर्ता इनपुट पर निर्भर नहीं करती हैं, लेकिन आपके कोड पर वह कर रही हैं जो यह करना है।

चार के वर्गमूल को कभी तीन का मूल्यांकन नहीं करना चाहिए। त्रुटि बस असंभव है। यदि ऐसा होता है, तो आपका प्रोग्राम तर्क बस टूट गया है। इससे कोई फर्क नहीं पड़ता कि हम अपने चारों ओर कितनी त्रुटि को संभालते हैं, यह ऐसा कुछ है जिसे विकास के दौरान पकड़ा जाना चाहिए या बिल्कुल नहीं। यदि हमने इस त्रुटि की जांच करने और इसे संभालने के लिए अपवाद हैंडलिंग का उपयोग किया है, तो अपवाद क्या होने जा रहा है? उपयोगकर्ता को बताएं "कार्यक्रम मौलिक रूप से टूट गया है। कभी इसका उपयोग न करें"?

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

लेकिन कुछ त्रुटियां, जैसे पढ़ने के लिए फ़ाइल खोलने में असमर्थ होना, संभव है । भले ही यह एक बुरी चीज हो सकती है अगर ऐसा होता है, तो हमें यह स्वीकार करना होगा कि यह हो सकता है। इसलिए हमें इसे संभालने की जरूरत है।

जोर लगाने वाले त्रुटियों को पकड़ने के लिए हैं जो संभवतः नहीं हो सकते हैं।


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

मेरा मानना ​​है कि इसलिए, जब कोई कार्यक्रम ऐसी चीज़ का पता लगाता है, जो उसकी आंतरिक स्थिति के साथ अनियमित रूप से गलत है, तो अपने कॉलर को यह दिखावा करने का अवसर देने के बजाय एक बार में समाप्त करना बेहतर है कि कुछ भी गलत नहीं है।

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


वे आपको अपनी मान्यताओं का परीक्षण करने में सक्षम बनाते हैं। उदाहरण के लिए, मान लीजिए कि आप गति की गणना करना चाहते हैं। आप शायद यह दावा करना चाहेंगे कि आपकी गणना प्रकाश की गति से कम है।

दावे विकास के लिए हैं, यह सुनिश्चित करने के लिए कि आप गड़बड़ न करें।


रिलीज़ के दौरान ज़रूरी नहीं कि विकास के दौरान स्थितियों को जाँचने के लिए ही आवेषण का उपयोग किया जाना चाहिए।

यहाँ एक बहुत ही सरल उदाहरण है कि कैसे विकास में जोर दिया जा सकता है।

A(char* p)
{
    if(p == NULL)
        throw exception;

    B(p);
    C(p);

}

B(char* p)
{
    assert(p != NULL);
    D(p);
    do stuff;
}

C(char* p)
{
    assert(p != NULL);
    D(p);
    do stuff;
}

D(char* p)
{
    assert(p != NULL);
    do stuff;
}

कॉल करने के बजाय "if (p == NULL) अपवाद छोड़ें?" 5 बार, आप इसे केवल एक बार कॉल करते हैं ताकि आप पहले से ही जानते हों कि यह B (), C (), और D () दर्ज करने पर NULL नहीं है। अन्यथा, एक जोर विकास चरण में निकल जाएगा क्योंकि आपने "कोड बदल दिया है!" एक "उपयोगकर्ता के इनपुट" के कारण नहीं।

यह रिलीज़ संस्करण में कोड को बहुत तेज़ी से चला सकता है क्योंकि आपको बस इतना करना है कि "-DNDEBUG" के साथ gcc का आह्वान करना है, इसलिए सभी कथनों को संकलित नहीं किया जाएगा और निष्पादन योग्य सभी "अनावश्यक चेक" हटा दिए जाएंगे।


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

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

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

एक प्रयोज्य स्टैंड बिंदु से मुखर भयानक होते हैं, भले ही वे "माना" न हों ऐसा हम सभी जानते हैं कि अंततः यह होगा ...

ठीक है ... सभी टिप्पणी और आग से मैं यहां मिल रहा हूं मुझे लगता है कि मुझे अपनी बात को और स्पष्ट करने की आवश्यकता है क्योंकि यह स्पष्ट रूप से समझा नहीं गया है।

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

अधिकांश उत्पादन कोड में मैंने देखा है कि मैंने इससे निपटने के लिए मुख्य रूप से दो तरीके देखे हैं, जो कोड में सभी जगह पर मक्खन लगाता है और फिर उत्पादन में एक अच्छा गुच्छा छोड़ देता है। यह उपयोगकर्ता के लिए एप्लिकेशन को बंद करने की असीम प्रवृत्ति है, मुझे अभी तक एक जोरदार ढंग से एक सिस्टम को विफल करने के लिए देखना है .... यह सिर्फ इसे विफल करता है ... बूम ... चला गया ... अंत उपयोगकर्ता सिर्फ "डब्ल्यूटीएफ" कह रहा है पता 0x330291ff पर एक जोरदार असफल त्रुटि है !!! "

दूसरा तरीका, यहां तक ​​कि अगर आप मुझसे पूछें, तो सबसे बुरा यह था कि जो कुछ भी फेंका गया था, उसे पकड़कर कालीन के नीचे छिपा दिया गया था (कभी इन भयानक कोशिशों को खाली ब्रेसेस के साथ देखा गया था !!)

NEETER WAY एक अच्छा स्थिर सिस्टम पाने के लिए काम करेगा। सुनिश्चित करें कि आप अपने बीटा कोड में एस्कॉर्ट्स का उपयोग कर सकते हैं और उन सभी को अपने उत्पादन कोड में हटा सकते हैं ... लेकिन नरक आप अपने सुरक्षा जाल को क्यों हटाएंगे क्योंकि यह उत्पादन है। मुझे बहुत आश्चर्य होगा कि ये सभी जाँच आपके सिस्टम के प्रदर्शन को खराब कर देंगे।

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

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

तो नहीं ... मैं जोर का प्रयोग नहीं करता ... मैं अपवादों का उपयोग करता हूं

और हाँ ... आमतौर पर उत्पादन में विफल रहने वाला कोड शायद ही कभी मेरा नाम शीर्ष पर होता है।


अपनी पोस्ट से, ऐसा लगता है कि आप मुखरता का उपयोग करने के विचार से असहमत नहीं हैं, बल्कि डिबग में जोर देने और उन्हें उत्पादन में सक्रिय नहीं होने के विचार से सहमत हैं।

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

अभिकथन का उपयोग करने से आप डिबग और रिलीज़ के बीच के व्यवहार को बदल सकते हैं।

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


"अपरिहार्य केल्विन और बूब्स" के उद्धरण का विरोध नहीं कर सकते। 180:

इस तरह एक खड़ी पहाड़ी से नीचे जाने से पहले, हमेशा अपने स्लेज को एक सुरक्षा जांच देना चाहिए।
सही।
सीट बेल्ट ? कोई नहीं।
संकेत? कोई नहीं।
ब्रेक? कोई नहीं।
स्टीयरिंग? कोई नहीं।
WHEEEEEE


जब डेटा सॉर्ट किया जाता है तो प्रदर्शन में भारी सुधार होता है, यह कारण है कि शाखा भविष्यवाणी दंड हटा दिया गया है, जैसा कि के जवाब में खूबसूरती से समझाया गया है।

अब, अगर हम कोड देखते हैं

if (data[c] >= 128)
    sum += data[c];

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

C , इस प्रकार C++ , कथन, जो x86 में सशर्त चाल निर्देश में सीधे (बिना किसी अनुकूलन के) संकलित करेगा, टर्नरी ऑपरेटर है ... ? ... : ... ... ? ... : ... इसलिए हम उपरोक्त कथन को समकक्ष में फिर से लिखते हैं:

sum += data[c] >=128 ? data[c] : 0;

पठनीयता को बनाए रखने के दौरान, हम स्पीडअप कारक की जांच कर सकते हैं।

इंटेल कोर i7 -2600K @ 3.4 गीगाहर्ट्ज और विजुअल स्टूडियो 2010 रिलीज मोड पर, बेंचमार्क है (मिस्टिकियल से प्रारूपित प्रारूप):

86

//  Branch - Random
seconds = 8.885

//  Branch - Sorted
seconds = 1.528

//  Branchless - Random
seconds = 3.716

//  Branchless - Sorted
seconds = 3.71

64

//  Branch - Random
seconds = 11.302

//  Branch - Sorted
 seconds = 1.830

//  Branchless - Random
seconds = 2.736

//  Branchless - Sorted
seconds = 2.737

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

अब वे जेनरेट की गई x86 असेंबली की जांच करके अधिक बारीकी से देखें। सादगी के लिए, हम दो कार्य max1 और max1 2 का उपयोग करते हैं।

max1 सशर्त शाखा का उपयोग करता है if... else ... :

int max1(int a, int b) {
    if (a > b)
        return a;
    else
        return b;
}

max2 टर्नरी ऑपरेटर का उपयोग करता है ... ? ... : ... ... ? ... : ... :

int max2(int a, int b) {
    return a > b ? a : b;
}

X86-64 मशीन पर, GCC -S नीचे असेंबली उत्पन्न करता है।

:max1
    movl    %edi, -4(%rbp)
    movl    %esi, -8(%rbp)
    movl    -4(%rbp), %eax
    cmpl    -8(%rbp), %eax
    jle     .L2
    movl    -4(%rbp), %eax
    movl    %eax, -12(%rbp)
    jmp     .L4
.L2:
    movl    -8(%rbp), %eax
    movl    %eax, -12(%rbp)
.L4:
    movl    -12(%rbp), %eax
    leave
    ret

:max2
    movl    %edi, -4(%rbp)
    movl    %esi, -8(%rbp)
    movl    -4(%rbp), %eax
    cmpl    %eax, -8(%rbp)
    cmovge  -8(%rbp), %eax
    leave
    ret

max2 निर्देश cmovge के उपयोग के कारण बहुत कम कोड का उपयोग करता है। लेकिन वास्तविक लाभ यह है कि jmp में शाखा कूदता, jmp शामिल नहीं है, यदि भविष्यवाणी का परिणाम सही नहीं है तो इसका महत्वपूर्ण प्रदर्शन दंड होगा।

तो एक सशर्त कदम बेहतर प्रदर्शन क्यों करता है?

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

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

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

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

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





c++ performance assertions