2013-04-02 3 views
27

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

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

क्या कोई इस दावा की पुष्टि या अस्वीकार कर सकता है?

+0

सामान्य जेआईटी संकलन प्रक्रिया में इन चरणों का समावेश होता है ..http: //publib.boulder.ibm.com/infocenter/java7sdk/v7r0/index.jsp? Topic =% 2Fcom.ibm.java.win.70.doc % 2Fdiag% 2 फंडांडिंग% 2Fjit_overview.html, लेकिन यह इस बारे में बात नहीं करता है कि कैसे गीट बड़े या छोटे मॉड्यूल को नियंत्रित करता है – AurA

उत्तर

1

मुझे वास्तव में यह समझ में नहीं आता कि यह कैसे काम करता है, लेकिन the link AurA provided पर आधारित, मुझे लगता है कि जेआईटी कंपाइलर को उसी बाइटकोड को संकलित करने के बजाय कम बाइट कोड को संकलित करना होगा, जो समान बाइटकोड को संकलित करने के बजाय समान है विभिन्न तरीकों से।

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

हालांकि मुझे संदेह है कि यदि आप बिना किसी कोड के अपने कोड को तोड़ते हैं तो कोई अच्छा प्रभाव नहीं पड़ेगा जो कोई कोड पुन: उपयोग नहीं करता है।

7

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

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

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

+0

"* यदि आप एक ही कोड लेते हैं और उन्हें बहुत छोटी विधियों में तोड़ देते हैं, जो कि जेआईटी की मदद नहीं करेगा बिल्कुल। * "=> मुझे नहीं लगता कि यह सटीक है (कम से कम हॉटस्पॉट पर)। – assylias

+0

कारण मैं कहता हूं कि मूल रूप से वही है जैसा कि @ रेवाल्ड ने किसी अन्य उत्तर में लिखा था, उद्धरण "लेकिन यदि विधियों की इनलाइनिंग केवल इसलिए मौजूद है क्योंकि एक बड़ी विधि को कई तरीकों से विभाजित किया गया है, कुछ भी प्राप्त नहीं हुआ है"। क्या आप विस्तारित करेंगे कि आपको लगता है कि यह सही नहीं है? –

+0

मैंने जो उदाहरण दिया है, उसे देखें - एक बड़ी विधि के साथ: कोई इनलाइनिंग नहीं, दो छोटी विधियों के साथ एक ही चीज़ कर रही है, दोनों इनलाइन हैं। – assylias

19

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

this page पर विभिन्न इनलाइनिंग विकल्पों को देखें।


संपादित

एक छोटे से विस्तार करने के लिए:

  • यदि एक विधि यह inlined हो जाएगी तो वहाँ छोटे तरीकों में कोड बंटवारे के लिए दंडित करने के लिए संभावना बहुत कम है छोटा है।
  • कुछ मामलों में, विभाजन विधियों के परिणामस्वरूप अधिक इनलाइनिंग हो सकती है।

उदाहरण (पूर्ण कोड एक ही लाइन नंबर यदि आप इसे करने की कोशिश करने के लिए)

package javaapplication27; 

public class TestInline { 
    private int count = 0; 

    public static void main(String[] args) throws Exception { 
     TestInline t = new TestInline(); 
     int sum = 0; 
     for (int i = 0; i < 1000000; i++) { 
      sum += t.m(); 
     } 
     System.out.println(sum); 
    } 

    public int m() { 
     int i = count; 
     if (i % 10 == 0) { 
      i += 1; 
     } else if (i % 10 == 1) { 
      i += 2; 
     } else if (i % 10 == 2) { 
      i += 3; 
     } 
     i += count; 
     i *= count; 
     i++; 
     return i; 
    } 
} 

निम्नलिखित JVM झंडे के साथ इस कोड को चलाते समय: -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation -XX:FreqInlineSize=50 -XX:MaxInlineSize=50 -XX:+PrintInlining (हाँ मैं का इस्तेमाल किया है मानों साबित मेरी मामला: m बहुत बड़ा है लेकिन दोनों रिफैक्टर m और m2 थ्रेसहोल्ड से नीचे हैं - अन्य मानों के साथ आपको एक अलग आउटपुट मिल सकता है)।

आपको लगता है कि m() और main() संकलित हो देखेंगे, लेकिन m() inlined नहीं प्राप्त करता है:

56 1    javaapplication27.TestInline::m (62 bytes) 
57 1 %   javaapplication27.TestInline::main @ 12 (53 bytes) 
      @ 20 javaapplication27.TestInline::m (62 bytes) too big 

तुम भी पुष्टि करते हैं कि m inlined नहीं है उत्पन्न विधानसभा निरीक्षण कर सकते हैं (मैं इन JVM झंडे का इस्तेमाल किया: -XX:+PrintAssembly -XX:PrintAssemblyOptions=intel) - यह इस तरह दिखेगा:

0x0000000002780624: int3 ;*invokevirtual m 
          ; - javaapplication27.TestInline::[email protected] (line 10) 

आप इस तरह कोड refactor हैं (मैं निकाला है यदि/बाकी एक अलग विधि में):

public int m() { 
    int i = count; 
    i = m2(i); 
    i += count; 
    i *= count; 
    i++; 
    return i; 
} 

public int m2(int i) { 
    if (i % 10 == 0) { 
     i += 1; 
    } else if (i % 10 == 1) { 
     i += 2; 
    } else if (i % 10 == 2) { 
     i += 3; 
    } 
    return i; 
} 

आप निम्न संकलन कार्रवाई देखेंगे:

60 1    javaapplication27.TestInline::m (30 bytes) 
60 2    javaapplication27.TestInline::m2 (40 bytes) 
      @ 7 javaapplication27.TestInline::m2 (40 bytes) inline (hot) 
63 1 %   javaapplication27.TestInline::main @ 12 (53 bytes) 
      @ 20 javaapplication27.TestInline::m (30 bytes) inline (hot) 
      @ 7 javaapplication27.TestInline::m2 (40 bytes) inline (hot) 

तो m2m में inlined हो जाता है, जो आप तो हम वापस मूल परिदृश्य के लिए कर रहे हैं उम्मीद करेंगे। लेकिन जब main संकलित हो जाता है, तो यह वास्तव में पूरी चीज को रेखांकित करता है। असेंबली स्तर पर, इसका मतलब है कि आपको कोई भी invokevirtual निर्देश नहीं मिलेगा। आपको इस तरह की रेखाएं मिलेंगी:

0x00000000026d0121: add ecx,edi ;*iinc 
             ; - javaapplication27.TestInline::[email protected] (line 33) 
             ; - javaapplication27.TestInline::[email protected] (line 24) 
             ; - javaapplication27.TestInline::[email protected] (line 10) 

जहां मूल रूप से सामान्य निर्देश "पारस्परिक" होते हैं।

निष्कर्ष

मैं कर रहा हूँ नहीं कह रही है कि इस उदाहरण प्रतिनिधि है, लेकिन यह कुछ बिंदुओं साबित करने के लिए लगता है:

  • छोटे पद्धति का उपयोग करके
  • छोटे तरीकों आम तौर पर होगा अपने कोड में पठनीयता में सुधार रेखांकित किया जा सकता है, इसलिए आप संभवतः अतिरिक्त विधि कॉल की लागत का भुगतान नहीं करेंगे (यह निष्पादन तटस्थ होगा)
  • छोटी विधियों का उपयोग के रूप में ऊपर

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

+1

प्रासंगिक विकल्प '-XX: InlineSmallCode = n' – Raedwald

+0

" इसलिए छोटे तरीकों का उपयोग करने से अधिक इनलाइनिंग की अनुमति मिलती है, जो कि अच्छा है "लेकिन यदि इन तरीकों में इनलाइनिंग मौजूद हैं, तो केवल इसलिए कि एक विधि को कई तरीकों से विभाजित किया गया है, कुछ भी नहीं है प्राप्त किया गया – Raedwald

+1

@ रेडवाल्ड 'FreqInlineSize' और 'MaxInlineSize' भी आप जो हासिल करने की कोशिश कर रहे हैं उसके आधार पर प्रासंगिक हैं। आपकी दूसरी टिप्पणी के बारे में: यह straigthforward नहीं है - अगर 'm1()' कॉल 'm2() 'और दोनों इनलाइनिंग सीमाओं के भीतर हैं, तो संपूर्ण' एम 1 + एम 2' शायद कॉलिंग साइट में रेखांकित किया जाएगा (अगर यह अक्सर पर्याप्त कहा जाता है आदि)। यदि दूसरी तरफ आप दो तरीकों को 'एम()' में विलय करते हैं जो सीमा से अधिक है, तो कुछ भी रेखांकित नहीं होता है और आप उस अनुकूलन को खो देते हैं। मैं बाद में एक उदाहरण जोड़ने की कोशिश करूंगा। – assylias

संबंधित मुद्दे

 संबंधित मुद्दे