एक विशाल "स्विच" बयान के बजाय एक ओओ दृष्टिकोण का उपयोग क्यों करें?


59

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

मैं ईमानदारी से इस के खिलाफ एक तर्क नहीं है ... क्योंकि मैं नहीं जानता कि वह किस बिल्ली के बारे में बात कर रहा है।
क्या वह सही है?
वह सिर्फ अपने गधे बाहर बात कर रहा है?
बस यहाँ सीखने की कोशिश कर रहा हूँ।


7
यदि आप असेंबली कोड को देखने के लिए .NET रिफलेक्टर जैसी किसी चीज़ का उपयोग करके सही हैं तो सत्यापित कर सकते हैं कि "cpu कूदने की मेज" है या नहीं।
FrustratedWithFormsDesigner

5
"स्विच स्टेटमेंट एक" सीपीयू जंप टेबल "के लिए संकलित करता है। इसलिए सभी शुद्ध-आभासी कार्यों के साथ सबसे खराब स्थिति वाला तरीका प्रेषण है। कोई भी वर्चुअल फ़ंक्शन सीधे तौर पर जुड़ा नहीं है। क्या आपने तुलना करने के लिए कोई कोड डंप किया है?
S.Lott

64
लोगों के लिए कोड मशीनों के लिए लिखा जाना चाहिए, अन्यथा हम विधानसभा में सब कुछ करेंगे।
maple_shaft

8
अगर वह बहुत ज्यादा नादान है, तो नथ से उसे उद्धृत करें: "हमें छोटी क्षमताओं के बारे में भूलना चाहिए, समय के बारे में 97% कहना चाहिए: समयपूर्व अनुकूलन सभी बुराई की जड़ है।"
डेव

12
रख-रखाव। एक शब्द के जवाब के साथ कोई अन्य प्रश्न मैं आपकी मदद कर सकता हूं?
मैट एलेन

जवाबों:


48

वह शायद एक पुराना सी हैकर है और हाँ, वह अपनी गांड से बात कर रहा है। .Net C ++ नहीं है; .नेट कंपाइलर बेहतर होता रहता है और सबसे चालाक हैक काउंटर-उत्पादक होते हैं, अगर आज नहीं तो अगले नेट संस्करण में। छोटे फ़ंक्शंस बेहतर हैं क्योंकि .Net JIT-s हर फ़ंक्शन को एक बार उपयोग करने से पहले। इसलिए, अगर किसी कार्यक्रम के LifeCycle के दौरान कुछ मामले कभी भी प्रभावित नहीं होते हैं, तो JIT- संकलन में कोई भी लागत नहीं आती है। किसी भी तरह, अगर गति एक मुद्दा नहीं है, तो अनुकूलन नहीं होना चाहिए। प्रोग्रामर के लिए पहले लिखें, कंपाइलर के लिए दूसरा। आपका सहकर्मी आसानी से आश्वस्त नहीं होगा, इसलिए मैं अनुभवजन्य रूप से साबित करूंगा कि बेहतर संगठित कोड वास्तव में तेज है। मैं उनके सबसे खराब उदाहरणों में से एक को चुनूंगा, उन्हें एक बेहतर तरीके से फिर से लिखूंगा, और फिर सुनिश्चित करें कि आपका कोड तेज है। चेरी-लेने अगर आप चाहिए। फिर इसे कुछ मिलियन बार चलाएं, प्रोफ़ाइल करें और उसे दिखाएं।

संपादित करें

बिल वैगनर ने लिखा:

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

EDIT2:

तो ... जाहिरा तौर पर एक स्विच स्टेटमेंट अगर / और स्टेटमेंट्स के एक समूह की तुलना में तेज़ और बेहतर है, क्योंकि एक तुलना लॉगरिदमिक है और दूसरी रैखिक है। http://sequence-points.blogspot.com/2007/10/why-is-switch-statement-faster-than-if.html

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

मुझे अभी भी यकीन नहीं है कि स्विच स्टेटमेंट बेहतर है।


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

27
@ जॉब क्या होगा अगर वह वास्तव में सही है? मुद्दा यह नहीं है कि वह गलत है, मुद्दा यह है कि वह सही है और इससे कोई फर्क नहीं पड़ता
रीन हेनरिक्स

2
भले ही वह लगभग 100% मामलों में सही था, लेकिन वह अभी भी हमारा समय बर्बाद कर रहा है।
जेरेमी

6
मैं अपनी आँखों को उस पृष्ठ से पढ़ने की कोशिश करना चाहता हूं जिससे आप जुड़े हुए हैं।
अटैकिंगहोबो

3
C ++ से क्या नफरत है? C ++ कंपाइलर भी बेहतर हो रहे हैं, और बड़े स्विच C ++ में C ++ की तरह ही खराब हैं, और बिल्कुल उसी कारण से। यदि आप पूर्व C ++ प्रोग्रामर से घिरे हुए हैं जो आपको दुःख देते हैं तो ऐसा नहीं है क्योंकि वे C ++ प्रोग्रामर हैं, ऐसा इसलिए है क्योंकि वे बुरे प्रोग्रामर हैं।
सेबेस्टियन रेडल

39

जब तक आपका सहकर्मी सबूत नहीं दे सकता है, कि यह परिवर्तन पूरे आवेदन के पैमाने पर एक वास्तविक औसत दर्जे का लाभ प्रदान करता है , यह आपके दृष्टिकोण (यानी बहुरूपता) से नीच है, जो वास्तव में ऐसा लाभ प्रदान करता है: स्थिरता।

Microoptimisation केवल तब किया जाना चाहिए, जब टोंटी को नीचे पिन किया जाता है। समयपूर्व अनुकूलन सभी बुराई की जड़ है

गति मात्रात्मक है। "दृष्टिकोण ए में दृष्टिकोण बी की तुलना में तेज है" में थोड़ी उपयोगी जानकारी है। सवाल " कितना तेज है? "।


2
एकदम सही। कभी यह दावा न करें कि कुछ तेज है, हमेशा मापें। और केवल तभी मापें जब अनुप्रयोग का वह भाग प्रदर्शन अड़चन हो।
किलन फ़ॉथ 25'11

6
-1 के लिए "समयपूर्व अनुकूलन सभी बुराई की जड़ है।" कृपया पूरे उद्धरण को प्रदर्शित करें , न कि केवल एक हिस्सा जो नथ की राय को पूर्वाग्रहित करता है।
वैकल्पिक

2
@mathepic: मैंने जानबूझकर इसे एक उद्धरण के रूप में पेश नहीं किया। हालांकि, यह वाक्य, मेरी निजी राय है, हालांकि मेरी रचना नहीं है। हालांकि यह ध्यान दिया जा सकता है कि c2 के लोग मूल ज्ञान के उस हिस्से पर विचार कर रहे हैं।
बैक 2 डोस

8
@alternative पूर्ण नथ उद्धरण "इसमें कोई संदेह नहीं है कि दक्षता की कब्र का दुरुपयोग होता है। प्रोग्रामर समय की बड़ी मात्रा में सोचने या बर्बाद करने के बारे में सोचते हैं, या उनके कार्यक्रमों के गैर-राजनीतिक भागों की गति, और दक्षता के लिए ये प्रयास वास्तव में होते हैं। डिबगिंग और रखरखाव पर विचार करते समय मजबूत नकारात्मक प्रभाव। हमें छोटी क्षमता के बारे में भूलना चाहिए, समय के 97% के बारे में कहना चाहिए: समय से पहले अनुकूलन सभी बुराई की जड़ है। " ओपी के सहकर्मी को पूरी तरह से बताता है। IMHO back2dos ने "समय से पहले अनुकूलन सभी बुराई की जड़ है" के साथ अच्छी तरह से संक्षेप में कहा
मार्क जूल

2
@ मर्कज 97% समय
वैकल्पिक

27

अगर यह तेज हो तो कौन परवाह करता है?

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

स्थिरता, हालांकि, खेल का उद्देश्य है, और एक विशाल स्विच स्टेटमेंट भी थोड़ा बनाए रखने योग्य नहीं है, आप नए लोगों को कोड के माध्यम से विभिन्न रास्तों की व्याख्या कैसे करते हैं? प्रलेखन को कोड के रूप में लंबे समय तक रहना होगा!

इसके अलावा, आपने तब प्रभावी रूप से इकाई परीक्षण में पूरी तरह से असमर्थता प्राप्त कर ली (इंटरफेस के संभावित अभाव का उल्लेख नहीं करने के लिए कई संभावित रास्ते), जो आपके कोड को और भी कम बनाए रखता है।

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


1
+ समयपूर्व अनुकूलन का एक बड़ा उदाहरण।
शेनके

निश्चित रूप से यह।
डेडएमजी

+1 के लिए 'एक विशाल स्विच स्टेटमेंट भी थोड़ा मेंटेन नहीं है'
कोरी हिंटन जूल

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

14

स्विच स्टेटमेंट से हट जाएं ...

इस तरह के स्विच स्टेटमेंट को प्लेग की तरह काट दिया जाना चाहिए क्योंकि यह ओपन बंद सिद्धांत का उल्लंघन करता है । यह टीम को मौजूदा कोड में परिवर्तन करने के लिए मजबूर करता है जब नई कार्यक्षमता को जोड़ने की आवश्यकता होती है, जैसा कि नए कोड को जोड़ने के विपरीत है।


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

3
यदि आपको OCP का उल्लंघन किए बिना एक OO प्रतिमान में मौजूदा प्रकारों में और अधिक संचालन जोड़ने की आवश्यकता है, तो मेरा मानना ​​है कि विज़िटर पैटर्न क्या है।
स्कॉट व्हिटलॉक

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

8
@ मर्टिन: क्या आपने कभी पार्सर लिखा है? बड़े स्विच-केस होना काफी आम है जो कि लुकहेड बफर में अगले टोकन पर स्विच करता है। आप हो सकता है उन लोगों के स्विच की जगह आभासी समारोह के साथ अगले टोकन के लिए कहता है, लेकिन है कि एक रखरखाव बुरा सपना हो जाएगा। यह दुर्लभ है, लेकिन कभी-कभी स्विच-केस वास्तव में बेहतर विकल्प होता है, क्योंकि यह कोड रखता है जिसे निकट निकटता में एक साथ पढ़ा / संशोधित किया जाना चाहिए।
nikie

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

8

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

जिन मीट्रिक की आपको देखभाल करने की आवश्यकता है वे हैं:

  • बदलाव की गति
  • ऐसा होने पर समस्या को खोजने की गति

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

तो निष्पादन समय के बारे में कैसे? कोई गति वृद्धि या कमी नहीं थी। निष्पक्ष होने के लिए हमारे प्रदर्शन को सिस्टम ड्राइवरों द्वारा थ्रॉटल किया गया था, इसलिए यदि ऑब्जेक्ट ओरिएंटेड समाधान वास्तव में धीमा था, तो हम इसे नहीं जान पाएंगे।

OO भाषा के लिए बड़े पैमाने पर स्विच स्टेटमेंट में क्या गलत है?

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

8

मैं प्रदर्शन तर्क नहीं खरीदता; यह कोड मेंटेनेंस के बारे में है।

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

अंगूठे का नियम: यदि स्विच किसी तरह TYPE पर किया जाता है, तो आपको शायद विरासत और आभासी कार्यों का उपयोग करना चाहिए। यदि स्विच एक निश्चित प्रकार के मान पर किया जाता है (उदाहरण के लिए, निर्देश opcode, जैसा कि ऊपर), तो इसे छोड़ देना ठीक है जैसा कि यह है।


5

आप मुझे समझा नहीं सकते हैं कि:

void action1()
{}

void action2()
{}

void action3()
{}

void action4()
{}

void doAction(int action)
{
    switch(action)
    {
        case 1: action1();break;
        case 2: action2();break;
        case 3: action3();break;
        case 4: action4();break;
    }
}

से काफी तेज है:

struct IAction
{
    virtual ~IAction() {}
    virtual void action() = 0;
}

struct Action1: public IAction
{
    virtual void action()    { }
}

struct Action2: public IAction
{
    virtual void action()    { }
}

struct Action3: public IAction
{
    virtual void action()    { }
}

struct Action4: public IAction
{
    virtual void action()    { }
}

void doAction(IAction& actionObject)
{
    actionObject.action();
}

इसके अतिरिक्त OO संस्करण केवल अधिक रखरखाव योग्य है।


8
कुछ चीजों के लिए और कम मात्रा में कार्यों के लिए, OO संस्करण बहुत नासमझ है। IAction के निर्माण में कुछ मूल्य को परिवर्तित करने के लिए किसी प्रकार का कारखाना होना चाहिए। कई मामलों में यह इसके बजाय उस मूल्य पर स्विच करने के लिए बहुत अधिक पठनीय है।
ज़ैन लिंक्स

@Zan Lynx: आपका तर्क बहुत सामान्य है। IAction ऑब्जेक्ट का निर्माण उतना ही मुश्किल है जितना कि एक्शन पूर्णांक को पुनः प्राप्त करना कठिन नहीं है। तो हम सामान्य बातचीत कर सकते हैं बिना जेनेरिक के। एक कैलकुलेटर पर विचार करें। यहाँ जटिलता में अंतर क्या है? उत्तर शून्य है। जैसा कि सभी क्रियाएं पूर्व-निर्मित हैं। आप उपयोगकर्ता से इनपुट और इसके पहले से ही एक कार्रवाई करते हैं।
मार्टिन यॉर्क

3
@ मर्टिन: आप एक जीयूआई कैलकुलेटर ऐप मान रहे हैं। आइए इसके बजाय एक एम्बेडेड सिस्टम पर C ++ के लिए लिखा गया कीबोर्ड कैलकुलेटर ऐप लें। अब आपके पास एक हार्डवेयर रजिस्टर से स्कैन-कोड पूर्णांक है। अब क्या कम जटिल है?
ज़ैन लिंक्स

2
@Martin: आप यह नहीं देखते हैं कि पूर्णांक -> लुकअप टेबल -> नई वस्तु का निर्माण -> कॉल वर्चुअल फ़ंक्शन पूर्णांक की तुलना में अधिक जटिल है -> स्विच -> फ़ंक्शन? आप उसे कैसे नहीं देखते हैं?
ज़ैन लिंक्स

2
@ मॉर्टिन: हो सकता है। इस बीच, एक लुकअप टेबल के बिना पूर्णांक से क्रिया () पर कॉल करने के लिए आपको IAction ऑब्जेक्ट कैसे मिलता है ।
ज़ैन लिंक्स

4

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

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


3

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


3

ओओपी कोड में एक स्विच स्टेटमेंट लापता कक्षाओं का एक मजबूत संकेत है

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


3

आम तौर पर मुझे इस शब्द से नफरत है, "समय से पहले अनुकूलन", लेकिन यह इसके बारे में बताता है। यह ध्यान देने योग्य है कि नुथ ने महत्वपूर्ण क्षेत्रों gotoमें कोड को गति देने के लिए बयानों का उपयोग करने के लिए धक्का देने के संदर्भ में इस प्रसिद्ध उद्धरण का उपयोग किया । यह महत्वपूर्ण है: महत्वपूर्ण पथ।

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

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

क्या वह सही है?

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

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

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

वह सिर्फ अपने गधे बाहर बात कर रहा है?

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

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

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


2

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


1
समय परीक्षण के साथ, एक कोड डंप यह दिखाने में मदद कर सकता है कि C ++ (और C #) विधि प्रेषण कार्य कैसे करता है।
S.Lott

2

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

उदाहरण के लिए। यदि आप के बीच स्विच कर रहे हैं Dog, Catऔर Elephant, और कभी-कभी Dogऔर Catएक ही मामला है, तो आप उन दोनों को एक सार वर्ग से विरासत में बना सकते हैं DomesticAnimalऔर उन फ़ंक्शन को सार वर्ग में डाल सकते हैं।

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


एक स्विच व्यवहार भी विरासत में मिल सकता है, यह डिफ़ॉल्ट मामले के माध्यम से है। "... का विस्तार बेसऑपरेशन वीजिटर" "डिफ़ॉल्ट: बेसऑपरेशन (नोड)" हो जाता है
सैमुअल डेनियलसन

0

"हमें छोटी क्षमताओं के बारे में भूलना चाहिए, समय के 97% के बारे में कहना चाहिए: समय से पहले अनुकूलन सभी बुराई की जड़ है"

डोनाल्ड नथ


0

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


1
गुम "नहीं"? सी # में, नहीं सभी फ़ंक्शन कॉल आभासी है। C # जावा नहीं है।
बेन वोइगट

0

आपका सहकर्मी उसकी पीठ के पीछे से बात नहीं कर रहा है, जहां तक ​​जंप टेबल के संबंध में टिप्पणी है। हालाँकि, इसका उपयोग करना खराब लेखन को सही ठहराने के लिए वह गलत है।

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

एक बड़े स्विच स्टेटमेंट को पढ़ना और बनाए रखना कठिन है। "मामलों" और कार्यों का एक शब्दकोश पढ़ने में बहुत आसान है। जैसा कि स्विच चालू हो जाता है, आपको और आपके सहयोगी को सीधे शब्दकोशों का उपयोग करने की सलाह दी जाएगी।


0

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

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

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

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

एक प्रमुख उदाहरण एक केंद्रीय ईवेंट हैंडलर है। उस स्थिति में आपके पास आम तौर पर घटनाओं को संभालने वाली कई जगहें नहीं होती हैं, बस एक (यह "केंद्रीय" क्यों है)। उन मामलों के लिए, आप उस बहुलता से लाभ नहीं लेते हैं जो एक बहुरूपी समाधान प्रदान करता है। एक पॉलीमॉर्फिक समाधान फायदेमंद होता है जब कई स्थान होते हैं जो एनालॉग switchस्टेटमेंट करते हैं। यदि आप निश्चित रूप से जानते हैं कि केवल एक होने जा रहा है, तो switch15 मामलों के साथ एक बयान एक पूरे वर्ग को डिजाइन करने की तुलना में अधिक सरल हो सकता है, जो ओवरराइड किए गए कार्यों के साथ 15 उपप्रकारों द्वारा विरासत में मिला है और उन्हें तत्काल करने के लिए एक कारखाना है, केवल तब एक कार्यक्रम में उपयोग किया जा सकता है पूरे सिस्टम में। उन मामलों में, नया उपप्रकार जोड़ना caseएक फ़ंक्शन में एक बयान जोड़ने की तुलना में बहुत अधिक थकाऊ है । अगर कुछ भी हो, तो मैं प्रदर्शन के लिए नहीं, स्थिरता के लिए तर्क दूंगा;switch इस एक अजीबोगरीब मामले में बयान जहां आपको व्यापकता से लाभ नहीं होता है।

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