java - क्या जावा स्थैतिक कॉल गैर स्थैतिक कॉल की तुलना में कम या ज्यादा महंगा है?




performance premature-optimization (8)

क्या कोई प्रदर्शन लाभ एक तरफ या दूसरा है? क्या यह कंपाइलर / वीएम विशिष्ट है? मैं हॉटस्पॉट का उपयोग कर रहा हूँ।


एक अंतर हो सकता है, और यह कोड के किसी भी विशेष टुकड़े के लिए किसी भी तरह से जा सकता है, और यह JVM की एक मामूली रिलीज के साथ भी बदल सकता है।

यह निश्चित रूप shreevatsa.wordpress.com/2008/05/16/… हिस्सा है shreevatsa.wordpress.com/2008/05/16/…


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

हालांकि, इससे आपके डिजाइन को प्रभावित नहीं किया जा सकता है - कोड को सबसे अधिक पढ़ने योग्य, प्राकृतिक तरीके से - और केवल इस तरह के सूक्ष्म अनुकूलन के बारे में चिंता करें यदि आपके पास सिर्फ कारण है (जो आप लगभग कभी नहीं करेंगे)।


जैसा कि पिछले पोस्टर ने कहा है: यह एक समयपूर्व अनुकूलन की तरह लगता है।

हालांकि, एक अंतर है (तथ्य यह है कि गैर स्थैतिक आविष्कारों को ऑपरल स्टैक पर कैली-ऑब्जेक्ट का अतिरिक्त धक्का की आवश्यकता होती है):

चूंकि स्थैतिक विधियों को ओवरराइड नहीं किया जा सकता है, इसलिए स्टैटिक विधि कॉल के लिए रनटाइम में कोई वर्चुअल लुकअप नहीं होगा । इसके परिणामस्वरूप कुछ परिस्थितियों में एक अवलोकन अंतर हो सकता है।

बाइट-कोड स्तर पर अंतर यह है कि एक गैर-स्थैतिक विधि कॉल INVOKEVIRTUAL , INVOKEINTERFACE या INVOKESPECIAL माध्यम से किया जाता है जबकि एक स्थिर विधि कॉल INVOKESTATIC माध्यम से किया जाता है।


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

1.) स्टेटिक विधियां पॉलिमॉर्फिक नहीं हैं, इसलिए जेवीएम के पास वास्तविक कोड निष्पादित करने के लिए कम निर्णय लेने हैं। यह हॉटस्पॉट की आयु में एक महत्वपूर्ण बिंदु है, क्योंकि हॉटस्पॉट इंस्टेंस विधि कॉल को अनुकूलित करेगा जिसमें केवल एक कार्यान्वयन साइट है, इसलिए वे वही करेंगे।

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


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


यह कंपाइलर / वीएम विशिष्ट है।

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

इसलिए संभवतः यह तब तक परेशान नहीं है जब तक कि आपने इसे अपने आवेदन में वास्तव में महत्वपूर्ण प्रदर्शन समस्या के रूप में नहीं पहचाना है। समयपूर्व अनुकूलन सभी बुराई आदि की जड़ है ...

हालांकि मैंने देखा है कि यह अनुकूलन निम्नलिखित परिस्थितियों में पर्याप्त प्रदर्शन वृद्धि प्रदान करता है:

  • मेमोरी एक्सेस के बिना एक बहुत ही सरल गणितीय गणना करने का तरीका
  • एक कड़े आंतरिक पाश में विधि प्रति सेकंड लाखों बार बुलाया जा रहा है
  • सीपीयू बाध्य अनुप्रयोग जहां प्रदर्शन के हर बिट mattered

यदि उपरोक्त आप पर लागू होता है, तो यह परीक्षण के लायक हो सकता है।

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


सिद्धांत रूप में, कम महंगी।

यदि आप ऑब्जेक्ट का उदाहरण बनाते हैं तो स्थिर प्रारंभिक कार्य भी किया जा रहा है, जबकि स्थैतिक विधियां आमतौर पर किसी भी निर्माता में किए गए प्रारंभिक कार्य नहीं करती हैं।

हालांकि, मैंने इसका परीक्षण नहीं किया है।


7 साल बाद ...

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

java -jar target/benchmark.jar

# -- snip --

Benchmark                        Mode  Cnt          Score         Error  Units
MyBenchmark.testInstanceMethod  thrpt  200  414036562.933 ± 2198178.163  ops/s
MyBenchmark.testStaticMethod    thrpt  200  417194553.496 ± 1055872.594  ops/s

आप यहां गिथूब पर कोड देख सकते हैं;

https://github.com/nfisher/svsi

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

package ca.junctionbox.svsi;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.infra.Blackhole;

class InstanceSum {
    public int sum(final int a, final int b) {
        return a + b;
    }
}

class StaticSum {
    public static int sum(final int a, final int b) {
        return a + b;
    }
}

public class MyBenchmark {
    private static final InstanceSum impl = new InstanceSum();

    @State(Scope.Thread)
    public static class Input {
        public int a = 1;
        public int b = 2;
    }

    @Benchmark
    public void testStaticMethod(Input i, Blackhole blackhole) {
        int sum = StaticSum.sum(i.a, i.b);
        blackhole.consume(sum);
    }

    @Benchmark
    public void testInstanceMethod(Input i, Blackhole blackhole) {
        int sum = impl.sum(i.a, i.b);
        blackhole.consume(sum);
    }
}




premature-optimization