जावा कितना महंगा है एक विधि कॉल


82

मैं एक शुरुआती हूं और मैंने हमेशा पढ़ा है कि कोड को दोहराना बुरा है। हालांकि, ऐसा लगता है कि ऐसा नहीं करने के लिए, आपको आमतौर पर अतिरिक्त विधि कॉल करना होगा। मान लीजिए कि मेरे पास निम्न वर्ग है

public class BinarySearchTree<E extends Comparable<E>>{
    private BinaryTree<E> root;
    private final BinaryTree<E> EMPTY = new BinaryTree<E>();
    private int count;
    private Comparator<E> ordering;

    public BinarySearchTree(Comparator<E> order){
        ordering = order;
        clear();
    }

    public void clear(){
        root = EMPTY;
        count = 0;
    }
}

क्या मेरे लिए वास्तविक तरीके से कॉल करने के बजाय निर्माणकर्ता में अपनी स्पष्ट () विधि में दो पंक्तियों को कॉपी और पेस्ट करना मेरे लिए अधिक इष्टतम होगा? यदि हां, तो इससे कितना फर्क पड़ता है? क्या होगा अगर मेरे निर्माता ने प्रत्येक के साथ 10 विधि कॉल किए, बस एक इंस्टेंस वैरिएबल को एक मान पर सेट किया जाए? सबसे अच्छा प्रोग्रामिंग अभ्यास क्या है?


4
लेकिन रुकिए, अगर आप अभी विधि लागू करते हैं, तो हम एक SECOND विधि कॉल में फेंक देंगे, बिल्कुल मुफ्त! केवल शिपिंग और हैंडलिंग का भुगतान करें! वास्तव में मुश्किल। वहाँ विधि कॉल में ओवरहेड है, जैसे कि लोड करने के लिए अधिक कोड होने में ओवरहेड है। कुछ बिंदु पर एक दूसरे की तुलना में अधिक महंगा हो जाता है। बताने का एकमात्र तरीका है अपने कोड को बेंचमार्क करना।
मार्क बी

11
समयपूर्व अनुकूलन 3 ... 2 ... 1

22
मैं इस पर गिरावट का कारण नहीं देखता - आदमी एक पूरी तरह से वैध सवाल पूछ रहा है। यह कुछ के लिए एक स्पष्ट जवाब हो सकता है लेकिन यह एक बुरा सवाल नहीं करता है!
माइकल बेरी

3
वास्तव में, यह एक बहुत ही वैध सवाल है, केवल एक सटीक डुप्लिकेट होने पर डाउन वोट का कारण हो सकता है।
अराफंगियन

1
क्षमा करें यदि यह स्पष्ट है, लेकिन मैं खुद से सीख रहा हूं, और मैं केवल कुछ महीनों के लिए ही हूं। कुछ चीजें जो मैंने सैंपल कोड में देखी हैं, मैंने पाया है कि मुझे अच्छा अभ्यास नहीं मिला है, इसलिए मैं सिर्फ डबल चेक करना चाहता हूं।
jhlu87

जवाबों:


74

क्या मेरे लिए वास्तविक तरीके से कॉल करने के बजाय निर्माणकर्ता में अपनी स्पष्ट () विधि में दो पंक्तियों को कॉपी और पेस्ट करना मेरे लिए अधिक इष्टतम होगा?

कंपाइलर उस ऑप्टिमाइज़ेशन को कर सकता है। और इसलिए जेवीएम कर सकता है। संकलक लेखक और जेवीएम लेखकों द्वारा उपयोग की जाने वाली शब्दावली "इनलाइन विस्तार" है।

यदि हां, तो इससे कितना फर्क पड़ता है?

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

क्या होगा यदि मेरे निर्माता ने हर एक के साथ 10 विधि कॉल किए जो केवल एक उदाहरण चर को एक मूल्य पर सेट कर सके?

फिर से, जो कि जेनरेट किए गए बाईटेकोड और जावा वर्चुअल मशीन द्वारा किए गए किसी भी रनटाइम अनुकूलन पर निर्भर करता है। यदि कंपाइलर / JVM विधि कॉल को इनलाइन कर सकता है, तो यह रनटाइम के दौरान नए स्टैक फ्रेम बनाने के ओवरहेड से बचने के लिए अनुकूलन का प्रदर्शन करेगा।

सबसे अच्छा प्रोग्रामिंग अभ्यास क्या है?

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


बेंचमार्क करने का एक अच्छा तरीका क्या है? क्या कोई सॉफ्टवेयर है जिसे मैं डाउनलोड कर सकता हूं या क्या आप इसका मतलब है कि प्रारंभ और अंत में सिर्फ System.nanoTime () का उपयोग करें और अंतर को प्रिंट करें?
jhlu87

System.nanoTime()या System.currentTimeMillisप्रोफाइलिंग करने का एक खराब तरीका है। आप इस Stackoverflow सवाल के जवाब से प्रोफाइलर्स की एक सूची प्राप्त कर सकते हैं । मैं VisualVM की सिफारिश करूंगा, क्योंकि यह अब JDK के साथ आता है।
विनीत रेनॉल्ड्स

2
@ jhlu87: मुझे लगता है कि आपको एक विधि कॉल के ओवरहेड का सटीक अनुमान प्राप्त करना बहुत मुश्किल होगा। माइक्रोबेन्चमार्किंग सही होने के लिए बहुत कठिन है और तब भी आम तौर पर चीजों की भव्य योजना में बहुत उपयोगी नहीं है। पढ़ें यह
कॉलिनड

@VineetReynolds जुड़ा हुआ प्रश्न मृत है।
मंद 8

19

अनुकूलन के बारे में बाकी सभी ने जो कहा है वह बिल्कुल सच है।

विधि को इनलाइन करने के लिए प्रदर्शन के दृष्टिकोण से कोई कारण नहीं है । यदि यह एक प्रदर्शन मुद्दा है, तो आपके JVM में JIT इसे इनलाइन करेगा। जावा में, विधि कॉल मुक्त करने के लिए इतने करीब हैं कि इसके बारे में सोचने लायक नहीं है।

यह कहा जा रहा है, यहाँ एक अलग मुद्दा है। अर्थात्, यह है एक ओवरराइड विधि (यानी, एक है कि नहीं है कॉल करने के लिए बुरा प्रोग्रामिंग अभ्यास final, staticया privateनिर्माता से)। (प्रभावी जावा, 2 एड।, पी। 89 में आइटम "शीर्षक और विरासत के लिए डिजाइन और दस्तावेज या इसे प्रतिबंधित करें"

अगर किसी का एक उपवर्ग कहते हैं तो क्या होता है BinarySearchTreeकहा जाता है LoggingBinarySearchTreeकि ओवरराइड की तरह कोड के साथ सभी सार्वजनिक तरीके:

public void clear(){
  this.callLog.addCall("clear");
  super.clear();
}

तब LoggingBinarySearchTreeनिर्माण कभी नहीं होगा! मुद्दा यह है कि है this.callLogहो जाएगा nullजब BinarySearchTreeनिर्माता चल रहा है, लेकिन clearकहा जाता हो जाता है कि ओवरराइड एक है, और यदि आप एक मिल जाएगा NullPointerException

ध्यान दें कि जावा और C ++ यहां भिन्न हैं: C ++ में, एक सुपरक्लास कंस्ट्रक्टर जो एक virtualविधि को कॉल करता है वह सुपरक्लास में परिभाषित एक को कॉल करता है, ओवरराइड नहीं। दोनों भाषाओं के बीच स्विच करने वाले लोग कभी-कभी इसे भूल जाते हैं।

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


1
मुझे नहीं लगता कि वह स्टाइल टिप्स को कोड करने के लिए कह रहा था, बल्कि यह जानने के लिए कि क्या मेथड कॉल महंगा है या नहीं
Asaf Mesika

3
उन्होंने पूछा कि "सबसे अच्छा प्रोग्रामिंग अभ्यास क्या है?" - सर्वोत्तम प्रथाओं के रूप में, यह पूरी तरह से प्रासंगिक है।
डैनियल मार्टिन

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

6

मैं निश्चित रूप से इसे छोड़ दूँगा। यदि आप clear()तर्क बदलते हैं तो क्या होगा ? उन सभी स्थानों को खोजना अव्यावहारिक होगा जहाँ आपने कोड की 2 पंक्तियों की प्रतिलिपि बनाई थी।


4

आम तौर पर बोलना (और एक शुरुआत के रूप में इसका मतलब हमेशा होता है!) आपको कभी भी माइक्रो-ऑप्टिमाइज़ेशन नहीं करना चाहिए, जैसे आप विचार कर रहे हैं। हमेशा इस तरह की चीजों पर कोड की पठनीयता का पक्ष लें।

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

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

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


3

सबसे अच्छा अभ्यास दो बार मापना और एक बार काटना है।

एक बार जब आपने समय अनुकूलन को बर्बाद कर लिया है, तो आप इसे फिर से वापस नहीं पा सकते हैं! (इसलिए इसे पहले मापें और अपने आप से पूछें कि क्या यह अनुकूलन के लायक है। आप कितना वास्तविक समय बचाएंगे?)

इस मामले में, जावा वीएम शायद पहले से ही अनुकूलन कर रहा है जिसके बारे में आप बात कर रहे हैं।


3

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


1

पैटर्न जो मैं अनुसरण करता हूं, यह है कि प्रश्न में यह विधि निम्नलिखित में से किसी एक को संतुष्ट करेगी या नहीं:

  • क्या इस वर्ग के बाहर यह विधि उपलब्ध होना सहायक होगा?
  • क्या इस विधि को अन्य विधियों में उपलब्ध कराना सहायक होगा?
  • क्या हर बार जब मुझे इसकी आवश्यकता होगी, तब इसे फिर से लिखना निराशाजनक होगा?
  • क्या कुछ मापदंडों के उपयोग के साथ विधि की बहुमुखी प्रतिभा को बढ़ाया जा सकता है?

यदि उपरोक्त में से कोई भी सत्य है, तो इसे उसी तरीके से लपेटा जाना चाहिए।


इन सवालों को पूछना आसान नहीं है और केवल पहले से ही इसकी विधि में रफ़ कोड डाल दिया है!
अराफंगियन

2
पूछने वाला जिज्ञासु होता है कि उसकी विधि कॉलों की क्या ग्रैन्युलैरिटी होनी चाहिए। एक पूर्णांक बढ़ाने के लिए अगर आप सिर्फ उपयोग कर सकते हैं एक विधि बनाने के लिए कोई जरूरत नहीं हैi++;
Peaches491

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

1

clear()जब यह पठनीयता में मदद करता है तब विधि रखें । अचूक कोड होने के कारण अधिक महंगा है।


1

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

"सभी बुराईयो की जड़ समयपूर्व इष्टतमीकरण है।" - डोनाल्ड नथ


0

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


0

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


0

जैसा कि दूसरों ने कहा है, विधि कॉल की लागत तुच्छ-से-नाडा है, क्योंकि संकलक आपके लिए इसे अनुकूलित करेगा।

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

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

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