2011-10-14 5 views
23

मुझे आश्चर्य है अगर JVM/javac बहुत चालाकअनुकूलन के दौरान जावा इनलाइन विधि (ओं) होगा?

में
// This line... 
string a = foo(); 

string foo() 
{ 
    return bar(); 
} 

string bar() 
{ 
    return some-complicated-string computation; 
} 

string a = bar(); 

या foo() रिहाई के मामले में करने के लिए पट्टी अनावश्यक कॉल (क्योंकि पहुँच योग्य नहीं कोड) मुड़ने के लिए है:

string a = foo(bar()); 

// bar is the same 
... 

string foo(string b) 
{ 
    if (debug) do-something-with(b); 
} 

मेरी भावना पहले उदाहरण के लिए हाँ है और दूसरे के लिए "इतना सुनिश्चित नहीं है", लेकिन क्या कोई मुझे इसकी पुष्टि करने के लिए कुछ पॉइंटर्स/लिंक दे सकता है?

+0

संक्षिप्त उत्तर: हां, जेवीएम इन प्रकार के अनुकूलन करेगा। (बशर्ते कि फ़ंक्शन कॉल polymorphic नहीं हैं और स्थिर रूप से निर्धारित किए जा सकते हैं।) – Mysticial

+2

@Mysticial: आपको ऐसा क्या लगता है? :) मैंने यह http://www.ibm.com/developerworks/java/library/j-benchmark1/index पढ़ा था।एचटीएमएल लेकिन मैं सकारात्मक नहीं हूं मेरे पास जवाब हैं। – Schultz9999

+0

मैंने कभी नहीं कहा कि यह हमेशा * अनुकूलन करेगा *। लेकिन आपके द्वारा दिया गया लिंक एक अच्छा पढ़ा है। – Mysticial

उत्तर

26

javac (कुछ स्थितियों में छोड़कर जब यह अनुकूलन कर सकते हैं: निरंतर तह और मृत-कोड उन्मूलन) बाईटकोड कि मूल जावा प्रोग्राम है कि बाईटकोड उत्पन्न की एक वफादार प्रतिनिधित्व है पेश करेंगे। हालांकि, जब JIT कंपाइलर का उपयोग करता है तो अनुकूलन JVM द्वारा किया जा सकता है।

पहले परिदृश्य यह JVM तरह लग रहा है के लिए इनलाइन किए जाने वाले (तहत तरीकेhere देख सकते हैं और JVM पर एक इनलाइन किए जाने वाले उदाहरण के लिए here देखें) का समर्थन करता है।

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

मैं क्या देख सकते हैं से, javacमृत-कोड उन्मूलन (दूसरे मामले के लिए उदाहरण देखें) और निरंतर तह का समर्थन करता है। निरंतर फोल्डिंग में, कंपाइलर निरंतर अभिव्यक्ति को पूर्ववत करेगा और रनटाइम के दौरान गणना करने के बजाए गणना मूल्य का उपयोग करेगा। उदाहरण के लिए:

public class ConstantFolding { 

    private static final int a = 100; 
    private static final int b = 200; 

    public final void baz() { 
     int c = a + b; 
    } 
} 

निम्नलिखित बाईटकोड को संकलित करता है: बाईटकोड एक sipush 300 बजाय aload के getfield और एक iadd है

Compiled from "ConstantFolding.java" 
public class ConstantFolding extends java.lang.Object{ 
private static final int a; 

private static final int b; 

public ConstantFolding(); 
    Code: 
    0: aload_0 
    1: invokespecial #1; //Method java/lang/Object."<init>":()V 
    4: return 

public final void baz(); 
    Code: 
    0: sipush 300 
    3: istore_1 
    4: return 

} 

ध्यान दें कि। 300 गणना मूल्य है। यह private final चर के मामले भी है। यदि a और b स्थिर नहीं थे, जिसके परिणामस्वरूप बाईटकोड हो जाएगा:

Compiled from "ConstantFolding.java" 
public class ConstantFolding extends java.lang.Object{ 
private final int a; 

private final int b; 

public ConstantFolding(); 
    Code: 
    0: aload_0 
    1: invokespecial #1; //Method java/lang/Object."<init>":()V 
    4: aload_0 
    5: bipush 100 
    7: putfield #2; //Field a:I 
    10: aload_0 
    11: sipush 200 
    14: putfield #3; //Field b:I 
    17: return 

public final void baz(); 
    Code: 
    0: sipush 300 
    3: istore_1 
    4: return 

} 

यहां भी एक sipush 300 प्रयोग किया जाता है।

public class InlineTest { 

    private static final boolean debug = false; 

    private void baz() { 
     if(debug) { 
     String a = foo(); 
     } 
    } 

    private String foo() { 
     return bar(); 
    } 

    private String bar() { 
     return "abc"; 
    } 
} 

जो निम्नलिखित बाईटकोड देता है::

Compiled from "InlineTest.java" 
public class InlineTest extends java.lang.Object{ 
private static final boolean debug; 

public InlineTest(); 
    Code: 
    0: aload_0 
    1: invokespecial #1; //Method java/lang/Object."<init>":()V 
    4: return 

private void baz(); 
    Code: 
    0: return 

private java.lang.String foo(); 
    Code: 
    0: aload_0 
    1: invokespecial #2; //Method bar:()Ljava/lang/String; 
    4: areturn 

private java.lang.String bar(); 
    Code: 
    0: ldC#3; //String abc 
    2: areturn 

} 

आप देख सकते हैं, foo है

दूसरे मामले (मृत-कोड उन्मूलन) के लिए, मैं निम्नलिखित परीक्षण कार्यक्रम का इस्तेमाल किया baz में बिल्कुल नहीं कहा जाता है क्योंकि if ब्लॉक के अंदर कोड प्रभावी रूप से "मृत" है।

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

बाइटकोड की निगरानी के अलावा, जेवीएम बाइटकोड के स्थिर विश्लेषण भी कर सकता है क्योंकि यह व्याख्यान और आगे अनुकूलन करने के लिए इसे लोड कर रहा है।

यदि आप जेवीएम द्वारा किए जाने वाले विशिष्ट प्रकार के अनुकूलन जानना चाहते हैं, तो this page ओरेकल में बहुत उपयोगी है। यह HotSpot JVM में उपयोग की जाने वाली प्रदर्शन तकनीकों का वर्णन करता है।

+0

और यदि फ़ंक्शन और 'डीबग' अंतिम हैं? –

+0

@ratchetfreak ओह, यह एक अच्छा बिंदु है। मुझे उसके बारे में पूरी तरह से याद नहीं था। यदि वे फाइनल हैं तो 'जावैक' कॉल को अनुकूलित करता है। मेरा जवाब संपादित करेंगे। यह बात बताने के लिए धन्यवाद। –

+0

और 'बार' फाइनल बनाने का क्या प्रभाव है? –

0

यदि आपने बार() में अपवाद फेंक दिया है और स्टैकट्रैक प्रिंट करते हैं तो आपको कॉल का पूरा पथ दिखाई देगा ... मुझे लगता है कि जावा उन सभी का सम्मान करते हैं।

दूसरा मामला वही है, डीबग आपके सिस्टम का एक चर है, सी ++ में परिभाषित नहीं है, इसलिए इसे पहले मूल्यांकन करना अनिवार्य है।

+0

डीबग "केवल एक चर" हो सकता है, या एक 'अंतिम' मान हो सकता है। –

+0

यदि डीबग अंतिम और गलत है तो इसे संकलित नहीं करना चाहिए (इस फ़ंक्शन में कोई वापसी नहीं) – yoprogramo

+0

असल में, यह विधि किसी भी मामले में संकलित नहीं होगी। –

2
एक ही कक्षा फ़ाइल में

javac भी बहुत कुछ (ज़रूरत से ज़्यादा इनलाइन करने bounds- को निकाल लिया अनुकूलन करने के लिए static और final (अन्य वर्ग फ़ाइलों inlined समारोह बदल सकता है) इनलाइन को

तथापि JIT में सक्षम हो जाएगा में सक्षम हो जाएगा और शून्य जांच, इत्यादि) क्योंकि यह कोड

+2

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

+0

बस इसे देखा और मैं वास्तव में सही था: 'हम ध्यान देते हैं कि एक कंपाइलर संकलन समय पर एक विधि इनलाइन का विस्तार नहीं कर सकता है।' तीसरा जेएलएस '13.4.22 विधि और कन्स्ट्रक्टर बॉडी ' – Voo

+0

@Voo वे' invokeinline 'जैसे कुछ जोड़ सकते थे और 'रिटर्नलाइन' ऑपरेशंस फ़ंक्शन कॉल की नकल करने के लिए जब वास्तव में इनलाइन किया गया है। लेकिन फ़ंक्शन ओवरहेड से बचने के लिए केवल 2 ऑपरेशंस जोड़ना शायद इसके लायक नहीं है –

2

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

आप विधियों को अंतिम और कुछ अन्य चाल बनाकर इनलाइनिंग की संभावनाओं को बढ़ा सकते हैं।

जावैक कुछ आदिम रूपरेखा करता है, ज्यादातर अंतिम/निजी तरीकों से, मुख्य रूप से कुछ सशर्त संकलन प्रतिमानों की सहायता करने के लिए लक्षित किया जाता है।

+0

सूर्य जेवीएम के साथ, विधि के (बाइटकोड) आकार पर एक ट्यूनेबल सीमा है जो रेखांकित की जाएगी। –

+0

हाँ, विभिन्न knobs, आंतरिक और बाहरी हैं। आमतौर पर इस बात पर एक सीमा होगी कि इनलाइनिंग आदि से कुल "ब्लोट" की अनुमति कैसे दी जाएगी। बाहरी रूप से दिखाई देने वाले knobs "सलाहकार" होने के लिए उपयुक्त हैं, हालांकि - कठिन मूल्य एक तरफ या दूसरे नहीं हैं। –

-1

मैं गलत हो सकता हूं, लेकिन मेरी भावना "सभी मामलों में नहीं" है। चूंकि आपके string bar() को उसी पैकेज में अन्य कक्षाओं द्वारा अधिभारित करके ओवरराइड किया जा सकता है। final विधियां अच्छे उम्मीदवार हैं, लेकिन यह जेआईटी पर निर्भर करता है।

एक और दिलचस्प नोट here है।

+1

डाउनवॉटिंग आसान है, लेकिन एक टिप्पणी लिखना मुश्किल है ... उन लोगों के लिए, जो इसे करते हैं, कृपया एक संदेश छोड़ दें, जहां मैं गलत हूं। –

1

जेवीएम सबसे अधिक संभावना है। सामान्य रूप से मानव पठनीयता के लिए अनुकूलित करना सबसे अच्छा है। जेवीएम रनटाइम अनुकूलन करने दें।

जेवीएम विशेषज्ञ Brian Goetz saysfinal पर उल्लिखित तरीकों पर कोई प्रभाव नहीं पड़ता है।

+0

शर्त पर 'x', या तो यह रेखांकित है या यह रेखांकित नहीं है। इसमें कोई अंतर नहीं है, क्या आपका "सबसे अधिक संभावना" मतलब "हां, यह रेखांकित है" या "नहीं, यह रेखांकित नहीं है"? – Pacerier

+0

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

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