2015-01-07 12 views
5

मैंने ब्रायन गोएट्ज़ द्वारा कुछ टिप्पणियों में पढ़ा है कि धारावाहिक लैम्बडास "गैर-निष्पादन योग्य लैम्बडास की तुलना में काफी अधिक प्रदर्शन लागत है"।जावा में सीरियलज़ेबल लैम्बडास का प्रदर्शन 8

अब मैं उत्सुक हूं: वह ओवरहेड कहां है और इसका कारण क्या है? क्या यह केवल लैम्ब्डा के तात्कालिकता, या आमंत्रण में भी प्रभावित होता है?

नीचे दिए गए कोड में, दोनों मामलों (कॉलएक्सस्टिंग इंस्टेंस() और कॉलविथन्यूइंस्टेंस()) "माईफंक्शन" की क्रमिकता से प्रभावित हो सकते हैं, या केवल दूसरा मामला?

interface MyFunction<IN, OUT> { 
    OUT call(IN arg); 
} 

void callExistingInstance() { 

    long toAdd = 1; 
    long value = 0; 

    final MyFunction<Long, Long> adder = (number) -> number + toAdd; 

    for (int i = 0; i < LARGE_NUMBER; i++) { 
     value = adder.call(value); 
    } 
} 

void callWithNewInstance() { 

    long value = 0; 

    for (int i = 0; i < LARGE_NUMBER; i++) { 
     long toAdd = 1; 

     MyFunction<Long, Long> adder = (number) -> number + toAdd; 

     value = adder.call(value); 
    } 
} 

उत्तर

3

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

+0

अच्छी व्याख्या के लिए धन्यवाद। –

+0

यह स्पष्टीकरण समस्या को मार नहीं रहा है। Deserializing ऑब्जेक्ट्स हमेशा महंगा प्रतिबिंब का उपयोग करेंगे चाहे आप लैम्ब्डा अभिव्यक्तियों का उपयोग करें या नहीं। लेकिन प्रश्न का कोड कुछ भी निराशाजनक नहीं है। – Holger

+0

प्रश्न अन्य प्रकार के लैमडब्स के विपरीत, धारावाहिक लैम्बडास को क्रमबद्ध करने और उपयोग करने के बारे में था। ओपी पूछ रहा था कि उन वस्तुओं के जीवन चक्र का कौन सा हिस्सा सामान्य लैम्ब्डा से कम प्रदर्शनकारी है। उनका कोड आमंत्रण और तत्कालता के बीच सही ढंग से अंतर करता है, और उसका प्रश्न सीधे उससे पूछता है। जैसा कि उत्तर इंगित करता है, उसके मामलों में से एक बिल्कुल प्रभावित होता है। Deserializing lambdas/अन्य वस्तुओं ("विशेष प्रतिबिंब") deserializing से बहुत अलग सामान/और यह स्वाभाविक रूप से अधिक महंगा है। – BadZen

2

आम तौर पर, लैम्ब्डा कार्यान्वयन के रनटाइम भाग में एक कक्षा उत्पन्न होगी जो मूल रूप से एक कार्यान्वयन विधि से मिलती है। ऐसी कक्षा उत्पन्न करने के लिए आवश्यक जानकारी बूटस्ट्रैप विधि आमंत्रण द्वारा रनटाइम पर LambdaMetafactory.metafactory पर दी जाती है।

सीरियलाइजेशन सक्षम करते समय, चीजें अधिक जटिल हो जाती हैं। सबसे पहले, संकलित कोड वैकल्पिक बूटस्ट्रैप विधि LambdaMetafactory.altMetafactory का उपयोग करेगा जो पैरामीटर सरणी के भीतर निर्दिष्ट झंडे के अनुसार varargs पैरामीटर को पार्स करने की कीमत पर अधिक लचीलापन प्रदान करता है।

तब उत्पन्न लैम्ब्डा वर्ग एक writeReplace विधि (Serializable documentation की दूसरी छमाही देखें) बना सकते हैं और सभी जानकारी लैम्ब्डा उदाहरण फिर से बनाने के लिए आवश्यक युक्त एक SerializedLambda उदाहरण वापस जाने के लिए है जो होना आवश्यक है। चूंकि लैम्ब्डा की कक्षा के एकल कार्यान्वयन विधि में केवल एक साधारण प्रतिनिधिमंडल कॉल होता है, writeReplace विधि और संबंधित निरंतर जानकारी जेनरेट क्लास के आकार को गुणा करेगी।

यह भी ध्यान देने योग्य बात है कि अपने वर्ग कि Serializable लैम्ब्डा उदाहरण बनाकर एक कृत्रिम विधि $deserializeLambda$ (लैम्ब्डा के writeReplace की प्रक्रिया के लिए एक समकक्ष के रूप में class documentation of SerializedLambda तुलना होगा लायक है। यही कारण है कि लेकिन अपनी कक्षाओं डिस्क उपयोग और लोडिंग समय में वृद्धि होगी (नहीं अपने उदाहरण कोड में) लैम्ब्डा अभिव्यक्ति का मूल्यांकन प्रभावित करते हैं।


, दोनों तरीकों बूटस्ट्रैपिंग और वर्ग पीढ़ी के रूप में उतना ही समय से प्रभावित होंगे लैम्ब्डा अभिव्यक्ति प्रति केवल एक बार होता। बाद में मूल्यांकन पर, the class generated on the first evaluation will be re-used and only a new instance created (if not even the instance is re-used) हम भेड़ के बच्चे के यहां भी एक बार के ऊपर की ओर बात कर रहे हैं दा अभिव्यक्ति एक लूप में निहित है, यह केवल पहले पुनरावृत्ति को प्रभावित करता है।

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

ध्यान दें कि अगर आप अपने दूसरी विधि में

final long toAdd = 1; 
MyFunction<Long, Long> adder = (number) -> number + toAdd; 

लिखा था (ध्यान दें स्पष्ट final संशोधक) मूल्य toAdd एक संकलन समय निरंतर हो सकता है और अभिव्यक्ति की तरह अगर आप (number) -> number + 1 लिखा था संकलित हो जाता है, यानी अब एक मूल्य पर कब्जा नहीं करेगा। फिर आपको प्रत्येक लूप पुनरावृत्ति (ओरेकल के जेवीएम के वर्तमान संस्करण के साथ) में एक ही लैम्ब्डा इंस्टेंस मिलेगा। तो सवाल यह है कि क्या एक नया उदाहरण बनाया गया है कभी-कभी संदर्भ के छोटे बिट्स पर निर्भर करता है। लेकिन आमतौर पर, प्रदर्शन प्रभाव अपेक्षाकृत छोटा होता है।

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