जावा परावर्तन प्रदर्शन


172

क्या क्लास कंस्ट्रक्टर परिणाम को कॉल करने के बजाय प्रतिबिंब का उपयोग करके किसी महत्वपूर्ण प्रदर्शन अंतर के कारण एक वस्तु का निर्माण होता है?


जवाबों:


169

हाँ बिल्कुल। प्रतिबिंब के माध्यम से एक वर्ग को देखना , परिमाण द्वारा , अधिक महंगा है।

प्रतिबिंब पर जावा के प्रलेखन का हवाला देते हुए :

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

यहाँ एक सरल परीक्षण है जिसे मैंने अपनी मशीन पर 5 मिनट में हैक कर लिया है, सन JRE 6u10 चला रहा है:

public class Main {

    public static void main(String[] args) throws Exception
    {
        doRegular();
        doReflection();
    }

    public static void doRegular() throws Exception
    {
        long start = System.currentTimeMillis();
        for (int i=0; i<1000000; i++)
        {
            A a = new A();
            a.doSomeThing();
        }
        System.out.println(System.currentTimeMillis() - start);
    }

    public static void doReflection() throws Exception
    {
        long start = System.currentTimeMillis();
        for (int i=0; i<1000000; i++)
        {
            A a = (A) Class.forName("misc.A").newInstance();
            a.doSomeThing();
        }
        System.out.println(System.currentTimeMillis() - start);
    }
}

इन परिणामों के साथ:

35 // no reflection
465 // using reflection

देखने और तात्कालिकता को एक साथ रखकर किया जाता है, और कुछ मामलों में लुकअप को दूर किया जा सकता है, लेकिन यह सिर्फ एक मूल उदाहरण है।

यहां तक ​​कि अगर आप सिर्फ तुरंत, आप अभी भी एक प्रदर्शन हिट हो:

30 // no reflection
47 // reflection using one lookup, only instantiating

फिर, वाईएमएमवी।


5
मेरी मशीन पर .newInstance () केवल एक Class.forName () कॉल स्कोर 30 या उसके साथ कॉल करें। वीएम संस्करण के आधार पर, अंतर एक उपयुक्त कैचिंग रणनीति के साथ आपके विचार से अधिक निकट हो सकता है।
सीन रेली

56
@Peter लॉरी ने नीचे बताया कि यह परीक्षण पूरी तरह से अमान्य था क्योंकि संकलक गैर-चिंतनशील समाधान का अनुकूलन कर रहा था (यह भी साबित कर सकता है कि कुछ भी नहीं किया गया है और लूप के लिए ऑप्टिमाइज़ किया गया है)। फिर से काम करने की जरूरत है और शायद एसओ को बुरी / भ्रामक जानकारी से हटा दिया जाना चाहिए। ऑप्टिमाइज़र को ऑप्टिमाइज़ करने से रोकने के लिए दोनों ही मामलों में एक सरणी में बनाई गई वस्तुओं को कैश करें। (यह चिंतनशील स्थिति में ऐसा नहीं कर सकता क्योंकि यह साबित नहीं कर सकता है कि निर्माता के दुष्प्रभाव नहीं हैं)
बिल K

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

4
यह शायद एक बेकार बेंचमार्क है। क्या करता है पर निर्भर करता है यदि यह दृश्यमान साइड इफेक्ट के साथ कुछ नहीं करता है, तो आपका बेंचमार्क केवल मृत कोड चलाता है।
nes1983

9
मैंने सिर्फ 35 बार JVM ऑप्टिमाइज़िंग प्रतिबिंब को देखा। एक लूप में बार-बार परीक्षण चलाना आप अनुकूलित कोड का परीक्षण कैसे करते हैं। पहला पुनरावृत्ति: 3045ms, दूसरा पुनरावृत्ति: 2941ms, तीसरा पुनरावृत्ति: 90ms, चौथा पुनरावृत्ति: 83ms। कोड: c.newInstance (i)। c एक कंस्ट्रक्टर है। गैर चिंतनशील कोड: नया A (i), जो 13, 4, 3 .. ms बार पैदावार देता है। तो हां, इस मामले में प्रतिबिंब धीमा था, लेकिन लगभग इतना धीमा नहीं था जितना लोग निष्कर्ष निकाल रहे हैं, क्योंकि हर परीक्षण जो मैं देख रहा हूं, वे बस एक बार जेवीएम को मशीन के साथ बाइट कोड को बदलने का अवसर दिए बिना परीक्षण चला रहे हैं। कोड।
माइक

87

हां, यह धीमा है।

लेकिन लानत # 1 नियम को याद रखें - प्रीमेच्योर ऑप्टिमाइज़ेशन सभी EVIL की जड़ है

(खैर, DRY के लिए # 1 के साथ जोड़ा जा सकता है)

मैं कसम खाता हूं, अगर कोई मेरे पास काम पर आया और मुझसे पूछा कि मैं अगले कुछ महीनों के लिए उनके कोड पर बहुत सतर्क रहूंगा।

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

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

जब मैं इस तरह के सवाल सुनता हूं, तो यह मुझे छोड़ देता है, लेकिन फिर मैं यह भूल जाता हूं कि हर किसी को सभी नियम सीखने से पहले ही गुजरना पड़ता है क्योंकि वे वास्तव में ऐसा करते हैं। किसी व्यक्ति द्वारा "ऑप्टिमाइज़्ड" किसी चीज़ पर डिबगिंग करने के बाद आपको यह मिलेगा।

संपादित करें:

इस धागे में एक दिलचस्प बात हुई। # 1 उत्तर की जाँच करें, यह उदाहरण है कि संकलक चीजों को अनुकूलित करने में कितना शक्तिशाली है। परीक्षण पूरी तरह से अमान्य है क्योंकि गैर-चिंतनशील तात्कालिकता को पूरी तरह से सचित्र किया जा सकता है।

सबक? जब तक आप एक साफ, बड़े करीने से लिखे गए समाधान को नहीं लिखते हैं, तब तक इसे अनुकूलित न करें और इसे बहुत धीमा साबित करें।


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

26
-1: चीजों को गलत तरीके से करने से बचना अनुकूलन नहीं है, यह सिर्फ चीजें कर रहा है। अनुकूलन वास्तविक या काल्पनिक प्रदर्शन चिंताओं के कारण चीजों को गलत, जटिल तरीके से कर रहा है।
सोरू

5
@ सोरू पूरी तरह से सहमत हैं। एक प्रविष्टि सॉर्ट के लिए एक सूची सूची पर एक लिंक्ड सूची का चयन बस चीजों को करने का सही तरीका है। लेकिन यह विशेष प्रश्न - मूल प्रश्न के दोनों पक्षों के लिए अच्छे उपयोग के मामले हैं, इसलिए सबसे उपयोगी समाधान के बजाय प्रदर्शन के आधार पर किसी एक को चुनना गलत होगा। मुझे यकीन नहीं है कि हम बिल्कुल असहमत हैं, इसलिए मुझे यकीन नहीं है कि आपने "-1" क्यों कहा।
बिल के

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

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

36

आपको लग सकता है कि A = new A () को JVM द्वारा अनुकूलित किया जा रहा है। यदि आप वस्तुओं को एक सरणी में रखते हैं, तो वे इतना अच्छा प्रदर्शन नहीं करते हैं। ;) निम्नलिखित प्रिंट ...

new A(), 141 ns
A.class.newInstance(), 266 ns
new A(), 103 ns
A.class.newInstance(), 261 ns

public class Run {
    private static final int RUNS = 3000000;

    public static class A {
    }

    public static void main(String[] args) throws Exception {
        doRegular();
        doReflection();
        doRegular();
        doReflection();
    }

    public static void doRegular() throws Exception {
        A[] as = new A[RUNS];
        long start = System.nanoTime();
        for (int i = 0; i < RUNS; i++) {
            as[i] = new A();
        }
        System.out.printf("new A(), %,d ns%n", (System.nanoTime() - start)/RUNS);
    }

    public static void doReflection() throws Exception {
        A[] as = new A[RUNS];
        long start = System.nanoTime();
        for (int i = 0; i < RUNS; i++) {
            as[i] = A.class.newInstance();
        }
        System.out.printf("A.class.newInstance(), %,d ns%n", (System.nanoTime() - start)/RUNS);
    }
}

यह सुझाव देता है कि मेरी मशीन पर लगभग 150 एनएस का अंतर है।


इसलिए आपने अभी-अभी ऑप्टिमाइज़र को मार दिया है, इसलिए अब दोनों संस्करण धीमे हैं। इसलिए, परावर्तन धीमा है।
gbjbaanb

13
@gbjbaanb यदि ऑप्टिमाइज़र स्वयं ही निर्माण का अनुकूलन कर रहा था तो यह एक वैध परीक्षण नहीं था। @ पीटर का परीक्षण इसलिए मान्य है क्योंकि यह वास्तव में सृजन के समय की तुलना करता है (आशावादी किसी भी वास्तविक दुनिया की स्थिति में काम करने में सक्षम नहीं होगा क्योंकि किसी भी वास्तविक दुनिया की स्थिति में आपको उन वस्तुओं की आवश्यकता होती है जो आप तात्कालिक हैं)।
बिल K

10
@ nes1983 जिस स्थिति में आप एक बेहतर बेंचमार्क बनाने का अवसर ले सकते थे। शायद आप कुछ रचनात्मक पेशकश कर सकते हैं, जैसे कि विधि के शरीर में क्या होना चाहिए।
पीटर लॉरी

1
मेरे मैक पर, Openjdk 7u4, अंतर 95ns बनाम 100ns है। सरणी में ए के भंडारण के बजाय, मैं हैशकोड स्टोर करता हूं। यदि आप कहते हैं -वरबोज: क्लास आप देख सकते हैं जब हॉटस्पॉट ए और साथ में स्पीडअप के निर्माण के लिए बायटेकोड उत्पन्न करता है।
रॉन

@PeterLawrey अगर मैं एक बार (एक कॉल Class.getDeclaredMethod) को ढूंढता हूं और फिर Method.invokeकई बार कॉल करता हूं ? क्या मैं एक बार प्रतिबिंब का उपयोग कर रहा हूं या जितनी बार मैं इसे आह्वान करता हूं? सवाल का पालन करें, क्या होगा अगर इसके बजाय Methodयह एक है Constructorऔर मैं Constructor.newInstanceकई बार करता हूं ?
TMJ

28

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

अनुप्रयोगों के कुछ उदाहरण जो कोड पीढ़ी का उपयोग करते हैं:

  • CGLIB द्वारा उत्पन्न परदे के पीछे की विधियों को जावा के गतिशील परदे के पीछे की तुलना में थोड़ा तेज है , क्योंकि CGLIB अपने प्रॉक्सी के लिए बायटेकोड उत्पन्न करता है, लेकिन गतिशील परदे के पीछे केवल प्रतिबिंब का उपयोग करता है ( मैंने विधि कॉलों में CGLIB को लगभग 10 गुना तेज किया, लेकिन परदे के पीछे का निर्माण धीमा था)।

  • जेसेरियल प्रतिबिंब का उपयोग करने के बजाय क्रमबद्ध वस्तुओं के क्षेत्रों को पढ़ने / लिखने के लिए बायटेकोड उत्पन्न करता है। JSerial की साइट पर कुछ बेंचमार्क हैं ।

  • मुझे 100% यकीन नहीं है (और मुझे अब स्रोत पढ़ने का मन नहीं है), लेकिन मुझे लगता है कि गुइसे निर्भरता इंजेक्शन करने के लिए बायटेकोड उत्पन्न करता है। यदि मैं गलत हूं तो मुझे सही करों।


27

"महत्वपूर्ण" पूरी तरह से संदर्भ पर निर्भर है।

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

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


24

परावर्तन के साथ कुछ ओवरहेड है, लेकिन यह आधुनिक वीएम पर बहुत छोटा है जितना कि यह हुआ करता था।

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


11

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

  Method md = null;     // Call while looking up the method at each iteration.
      millis = System.currentTimeMillis( );
      for (idx = 0; idx < CALL_AMOUNT; idx++) {
        md = ri.getClass( ).getMethod("getValue", null);
        md.invoke(ri, null);
      }

      System.out.println("Calling method " + CALL_AMOUNT+ " times reflexively with lookup took " + (System.currentTimeMillis( ) - millis) + " millis");



      // Call using a cache of the method.

      md = ri.getClass( ).getMethod("getValue", null);
      millis = System.currentTimeMillis( );
      for (idx = 0; idx < CALL_AMOUNT; idx++) {
        md.invoke(ri, null);
      }
      System.out.println("Calling method " + CALL_AMOUNT + " times reflexively with cache took " + (System.currentTimeMillis( ) - millis) + " millis");

में परिणाम होगा:

[जावा] लुकिंग विधि से १०००००० बार कॉलिंग लुकअप के साथ ५६१। मिली

[जावा] कॉलिंग विधि १०००००० बार कैश के साथ प्रतिपल २ing० मिली


पुन: उपयोग विधि / निर्माता वास्तव में उपयोगी है और मदद करता है, लेकिन ध्यान दें कि ऊपर दिए गए परीक्षण सामान्य बेंचमार्किंग समस्याओं (कोई वार्मअप नहीं है, इसलिए विशेष रूप से पहले लूप ज्यादातर JVM / JIT वार्मअप समय को मापते हैं) के कारण सार्थक संख्या नहीं देते हैं।
स्टेक्समैन

7

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

ध्यान रखें कि इस धागे में माइक्रोबेंचमार्क गहरी खामियां हैं, इसलिए उन्हें नमक के दाने के साथ लें। अब तक कम से कम त्रुटिपूर्ण पीटर लॉरी का है: यह विधियों को ठीक करने के लिए वार्मअप चलाता है, और यह (सचेत रूप से) यह सुनिश्चित करने के लिए विश्लेषण से पराजित होता है कि आवंटन वास्तव में हो रहे हैं। यहां तक ​​कि एक की अपनी समस्याएं हैं, हालांकि: उदाहरण के लिए, सरणी स्टोर की जबरदस्त संख्या से कैश को हराने और बफ़र्स को स्टोर करने की उम्मीद की जा सकती है, इसलिए यदि आपके आवंटन बहुत तेज हैं, तो यह ज्यादातर मेमोरी बेंचमार्क होगा। (हालांकि पीटर को निष्कर्ष प्राप्त करने के लिए कुडोस: हालांकि यह अंतर "2.5x" के बजाय "150ns" है। मुझे संदेह है कि वह एक जीवित व्यक्ति के लिए इस तरह का काम करता है।)


7

दिलचस्प रूप से पर्याप्त है, सेटल करने योग्य (सच्चा), जो सुरक्षा जांच को समाप्त करता है, लागत में 20% की कमी है।

बिना असफल (सत्य)

new A(), 70 ns
A.class.newInstance(), 214 ns
new A(), 84 ns
A.class.newInstance(), 229 ns

असफल (सत्य) के साथ

new A(), 69 ns
A.class.newInstance(), 159 ns
new A(), 85 ns
A.class.newInstance(), 171 ns

1
मुझे सिद्धांत रूप में स्पष्ट लगता है। क्या इन संख्याओं को रैखिक रूप से स्केल किया जाता है, जब 1000000इनवॉइस चल रहा हो ?
लुकास एडर

वास्तव setAccessible()में सामान्य रूप से बहुत अधिक अंतर हो सकता है, खासकर कई तर्कों वाले तरीकों के लिए, इसलिए इसे हमेशा कहा जाना चाहिए।
स्टेक्समैन

6

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


1
+1 मुझे एक ऐसा ही अनुभव हुआ। यह सुनिश्चित करने के लिए अच्छा है कि केवल प्रतिबिंब का उपयोग करें यदि यह बिल्कुल आवश्यक है।
रायन टेम्स

उदाहरण के लिए AOP आधारित पुस्तकालयों को प्रतिबिंब की आवश्यकता होती है।
गौरव

4

DoReflection () में Class.forName ("misc.A") की वजह से ओवरहेड होता है (जिसमें क्लास लुकिंग की आवश्यकता होती है, संभवतः नएइनस्टांस () के बजाय क्लास में बुलाया गया था)। मैं सोच रहा हूं कि क्या लग रहा होगा यदि Class.forName ("misc.A") फॉर-लूप के बाहर केवल एक बार किया जाता है, यह वास्तव में लूप के हर आह्वान के लिए नहीं किया जाता है।


1

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

देखिये यह सरल परीक्षण:

public class TestSpeed {
    public static void main(String[] args) {
        long startTime = System.nanoTime();
        Object instance = new TestSpeed();
        long endTime = System.nanoTime();
        System.out.println(endTime - startTime + "ns");

        startTime = System.nanoTime();
        try {
            Object reflectionInstance = Class.forName("TestSpeed").newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        endTime = System.nanoTime();
        System.out.println(endTime - startTime + "ns");
    }
}

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

3
इसके अलावा: आपको एक उपयोगी बेंचमार्क प्राप्त करने के लिए प्रत्येक कार्य को कई बार निष्पादित करने की आवश्यकता है: सबसे पहले सभी क्रियाओं को बहुत मज़बूती से मापा जाता है और दूसरी बात यह है कि उपयोगी नंबर प्राप्त करने के लिए आपको हॉटस्पॉट वीएम को गर्म करना होगा।
जोकिम सॉयर

1

अक्सर आप Apache commons BeanUtils या PropertyUtils का उपयोग कर सकते हैं जो आत्मनिरीक्षण करते हैं (मूल रूप से वे कक्षाओं के बारे में मेटा डेटा को कैश करते हैं ताकि उन्हें हमेशा प्रतिबिंब का उपयोग करने की आवश्यकता न हो)।


0

मुझे लगता है कि यह इस बात पर निर्भर करता है कि लक्ष्य विधि कितनी हल्की / भारी है। यदि लक्ष्य विधि बहुत हल्की है (जैसे गेट्टर / सेटर), तो यह 1 ~ 3 गुना धीमी हो सकती है। यदि लक्ष्य विधि में लगभग 1 मिलीसेकंड या उससे अधिक का समय लगता है, तो प्रदर्शन बहुत करीब होगा। यहाँ परीक्षण मैंने जावा 8 और परावर्तन के साथ किया है :

public class ReflectionTest extends TestCase {    
    @Test
    public void test_perf() {
        Profiler.run(3, 100000, 3, "m_01 by refelct", () -> Reflection.on(X.class)._new().invoke("m_01")).printResult();    
        Profiler.run(3, 100000, 3, "m_01 direct call", () -> new X().m_01()).printResult();    
        Profiler.run(3, 100000, 3, "m_02 by refelct", () -> Reflection.on(X.class)._new().invoke("m_02")).printResult();    
        Profiler.run(3, 100000, 3, "m_02 direct call", () -> new X().m_02()).printResult();    
        Profiler.run(3, 100000, 3, "m_11 by refelct", () -> Reflection.on(X.class)._new().invoke("m_11")).printResult();    
        Profiler.run(3, 100000, 3, "m_11 direct call", () -> X.m_11()).printResult();    
        Profiler.run(3, 100000, 3, "m_12 by refelct", () -> Reflection.on(X.class)._new().invoke("m_12")).printResult();    
        Profiler.run(3, 100000, 3, "m_12 direct call", () -> X.m_12()).printResult();
    }

    public static class X {
        public long m_01() {
            return m_11();
        }    
        public long m_02() {
            return m_12();
        }    
        public static long m_11() {
            long sum = IntStream.range(0, 10).sum();
            assertEquals(45, sum);
            return sum;
        }    
        public static long m_12() {
            long sum = IntStream.range(0, 10000).sum();
            assertEquals(49995000, sum);
            return sum;
        }
    }
}

पूरा परीक्षण कोड GitHub: ReflectionTest.java पर उपलब्ध है

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