क्या C / C ++ की तुलना में प्रदर्शन के लिए Java को "ट्वीक" करना ज्यादा कठिन है? [बन्द है]


11

क्या जेवीएम का "जादू" जावा में माइक्रो-ऑप्टिमाइज़ेशन पर एक प्रोग्रामर के प्रभाव को बाधित करता है? मैंने हाल ही में C ++ में पढ़ा है कभी-कभी डेटा सदस्यों का ऑर्डर अनुकूलन प्रदान कर सकता है (दी गई, माइक्रोसेकंड वातावरण में) और मैंने अनुमान लगाया कि जब जावा से प्रदर्शन निचोड़ने की बात आती है तो एक प्रोग्रामर के हाथ बंधे होते हैं?

मैं सराहना करता हूं कि एक सभ्य एल्गोरिथ्म अधिक गति-लाभ प्रदान करता है, लेकिन एक बार जब आपके पास सही एल्गोरिथ्म है तो जावा को जेवीएम नियंत्रण के कारण मोड़ना कठिन है?

यदि नहीं, तो लोग उदाहरण दे सकते हैं कि आप जावा (सरल संकलक झंडे के अलावा) में किन चालों का उपयोग कर सकते हैं।


14
सभी जावा ऑप्टिमाइजेशन के पीछे मूल सिद्धांत यह है: जेवीएम ने शायद पहले से ही बेहतर कर लिया है। अनुकूलन में ज्यादातर समझदार प्रोग्रामिंग प्रथाओं का पालन करना और सामान्य चीजों से बचना होता है जैसे लूप में स्ट्रैटनिंग स्ट्रिंग्स।
रॉबर्ट हार्वे

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

1
"जावा प्रदर्शन ट्रिक्स" के लिए, अध्ययन के लायक हैं: प्रभावी जावा , एंजेलिका लैंगर लिंक - जावा सिद्धांत और व्यवहार में ब्रायन गोएट्ज द्वारा जावा प्रदर्शन और प्रदर्शन से संबंधित लेख और थ्रेडिंग हल्की श्रृंखला यहां
gnat

2
टिप्स और ट्रिक्स के बारे में अत्यधिक सावधान रहें - JVM, ऑपरेटिंग सिस्टम और हार्डवेयर चालें - आप प्रदर्शन ट्यूनिंग कार्यप्रणाली सीखने और अपने विशेष वातावरण के लिए एन्हांसमेंट लागू करने के लिए सबसे अच्छा हैं :-)
मार्टिजेन वर्बर्ग

कुछ मामलों में, एक VM रन टाइम पर अनुकूलन कर सकता है जो संकलन-समय पर बनाने के लिए अव्यावहारिक है। प्रबंधित मेमोरी का उपयोग करने से प्रदर्शन में सुधार हो सकता है, हालाँकि इसमें अक्सर मेमोरी मेमोरी भी अधिक होगी। अप्रयुक्त मेमोरी को ASAP के बजाय सुविधाजनक होने पर मुक्त किया जाता है।
ब्रायन

जवाबों:


5

निश्चित रूप से, माइक्रो-ऑप्टिमाइज़ेशन स्तर पर जेवीएम कुछ ऐसी चीजें करेगा जो विशेष रूप से सी और सी ++ की तुलना में आपके पास थोड़ा नियंत्रण होगा।

दूसरी ओर, सी और सी ++ के साथ संकलक व्यवहार की विविधता विशेष रूप से किसी भी प्रकार के अस्पष्ट पोर्टेबल तरीके (यहां तक ​​कि संकलक संशोधनों के पार) में सूक्ष्म-अनुकूलन करने की आपकी क्षमता पर कहीं अधिक नकारात्मक प्रभाव डालेगी।

यह इस बात पर निर्भर करता है कि आप किस तरह का प्रोजेक्ट कर रहे हैं, आप किस पर्यावरण को लक्षित कर रहे हैं, आदि। और अंत में, यह वास्तव में कोई फर्क नहीं पड़ता क्योंकि आप एल्गोरिदम / डेटा संरचना / प्रोग्राम डिज़ाइन ऑप्टिमाइज़ेशन से बेहतर परिमाण के कुछ आदेश प्राप्त कर रहे हैं।


यह बहुत मायने रखता है जब आप पाते हैं कि आपका ऐप कोर के पार नहीं है
जेम्स

@ नाम - विस्तृत देखभाल?
तेलस्टिन

1
एक शुरुआत के लिए यहां देखें: मैकेनिकल-
जेम्स

1
@ जेम्स, कोर के पार स्केलिंग का कार्यान्वयन भाषा (पायथन को छोड़कर!) के साथ बहुत कम होता है, और, एप्लिकेशन आर्किटेक्चर के साथ अधिक करना है।
जेम्स एंडरसन

29

माइक्रो-ऑप्टिमाइज़ेशन लगभग समय के लायक नहीं होते हैं, और लगभग सभी आसान कंपाइलर्स और रनटाइम द्वारा स्वचालित रूप से किए जाते हैं।

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

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

यदि नहीं, तो लोग उदाहरण दे सकते हैं कि आप जावा (सरल संकलक झंडे के अलावा) में किन चालों का उपयोग कर सकते हैं।

संकलक झंडे जावा में अप्रासंगिक हैं क्योंकि जावा संकलक लगभग कोई अनुकूलन नहीं करता है; रनटाइम करता है।

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


1
+1: मूल रूप से जो मैं अपने उत्तर में लिख रहा था, शायद बेहतर सूत्रीकरण।
क्लेम

1
+1: बहुत अच्छे बिंदु, बहुत ही संक्षिप्त तरीके से समझाया गया है: "यह काफी कठिन है ... लेकिन अगर सही किया जाता है, तो यह पूरी तरह से लुभावनी प्रदर्शन का कारण बन सकता है। बेशक आप इसके लिए सभी प्रकार के भयानक कीड़े की क्षमता का भुगतान करते हैं। । "
जियोर्जियो

1
@ मर्टिनबा: यह अधिक है कि आप स्मृति प्रबंधन को अनुकूलित करने के लिए भुगतान करते हैं। यदि आप स्मृति प्रबंधन को अनुकूलित करने का प्रयास नहीं करते हैं, तो C ++ मेमोरी प्रबंधन मुश्किल नहीं है (इसे पूरी तरह से STL के माध्यम से बचें या RAII का उपयोग करके इसे अपेक्षाकृत आसान बनाएं)। बेशक, CII में RAII लागू करने से जावा (यानी, क्योंकि जावा आपके लिए इसे संभालता है) में कुछ भी नहीं करने की तुलना में कोड की अधिक लाइनें लगती हैं।
ब्रायन

3
@ मर्टिन बा: मूल रूप से हाँ। डैंग्लिंग पॉइंटर्स, बफर ओवरफ्लो, अनइंस्टाल्ड पॉइंटर्स, पॉइंटर अंकगणित में त्रुटियां, वे सभी चीजें जो मैन्युअल मेमोरी मैनेजमेंट के बिना मौजूद नहीं हैं। और मेमोरी एक्सेस का अनुकूलन करने के लिए आपको बहुत सारे मैनुअल मेमोरी मैनेजमेंट करने की आवश्यकता होती है ।
माइकल बोर्गवर्ड

1
कुछ चीजें हैं जो आप जावा में कर सकते हैं। ऑब्जेक्ट ऑब्जेक्ट पूलिंग है, जो ऑब्जेक्ट्स की मेमोरी लोकलिटी को अधिकतम करता है (C ++ के विपरीत जहां यह मेमोरी लोकेलिटी की गारंटी दे सकता है)।
RokL

5

[...] (दी गई, माइक्रोसेकंड पर्यावरण में) [...]

यदि हम लाखों-अरबों चीजों को लूप कर रहे हैं, तो माइक्रो-सेकंड जुड़ जाते हैं। C ++ से एक व्यक्तिगत vtune / माइक्रो-ऑप्टिमाइज़ेशन सत्र (कोई एल्गोरिदम सुधार नहीं):

T-Rex (12.3 million facets):
Initial Time: 32.2372797 seconds
Multithreading: 7.4896073 seconds
4.9201039 seconds
4.6946372 seconds
3.261677 seconds
2.6988536 seconds
SIMD: 1.7831 seconds
4-valence patch optimization: 1.25007 seconds
0.978046 seconds
0.970057 seconds
0.911041 seconds

"मल्टीथ्रेडिंग", "SIMD" (कंपाइलर को हराने के लिए हस्तलिखित), और 4-वैलेंस पैच ऑप्टिमाइज़ेशन के अलावा सब कुछ माइक्रो-लेवल मेमोरी ऑप्टिमाइज़ेशन थे। साथ ही 32 सेकंड के शुरुआती समय से शुरू होने वाला मूल कोड पहले से ही काफी थोड़ा (सैद्धांतिक रूप से इष्टतम एल्गोरिथम जटिलता) अनुकूलित किया गया था और यह हालिया सत्र है। इस हालिया सत्र से बहुत पहले के मूल संस्करण को संसाधित होने में 5 मिनट का समय लगा।

स्मृति दक्षता का अनुकूलन एक एकल-थ्रेडेड संदर्भ में परिमाण के आदेशों के लिए कई बार कहीं से भी मदद कर सकता है, और मल्टीथ्रेडेड संदर्भों में अधिक (मिश्रण में कई थ्रेड्स के साथ एक कुशल मेमोरी प्रतिनिधि के लाभ अक्सर गुणा करते हैं)।

माइक्रो-ऑप्टिमाइज़ेशन के महत्व पर

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

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

एल्गोरिदम

मुझे लगता है कि माइक्रो-ऑप्टिमाइज़ेशन महत्वपूर्ण नहीं है जब तक कि प्रदर्शन मिनटों से सेकंड के स्तर पर महत्वपूर्ण नहीं है, उदाहरण के लिए, या घंटे से मिनट। यदि हम बबल सॉर्ट की तरह एक भयानक एल्गोरिथ्म लेते हैं और इसे उदाहरण के रूप में बड़े पैमाने पर इनपुट पर उपयोग करते हैं, और फिर मर्ज सॉर्ट के एक बुनियादी कार्यान्वयन के लिए इसकी तुलना करते हैं, तो पूर्व में प्रक्रिया में महीनों लग सकते हैं, बाद में 12 मिनट हो सकते हैं। द्विघात बनाम रैखिकतात्मक जटिलता।

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

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

मुझे लगता है कि यह अंतर संभवतः 12 मिनट और 15 सेकंड के बीच के अधिकांश लोगों के लिए नगण्य है, और इसलिए माइक्रो-ऑप्टिमाइज़ेशन को अक्सर बेकार माना जाता है क्योंकि यह अक्सर केवल मिनट और सेकंड के बीच का अंतर होता है, और मिनट और महीने नहीं। दूसरा कारण मुझे लगता है कि इसे बेकार माना जाता है, यह अक्सर उन क्षेत्रों पर लागू होता है जो मायने नहीं रखते हैं: कुछ छोटे क्षेत्र जो कि लूप और क्रिटिकल भी नहीं हैं, जो कुछ संदिग्ध 1% अंतर पैदा करते हैं (जो बहुत अच्छी तरह से सिर्फ शोर हो सकता है)। लेकिन ऐसे लोगों के लिए जो इस प्रकार के समय के मतभेदों की परवाह करते हैं और इसे मापने और सही करने के लिए तैयार हैं, मुझे लगता है कि यह स्मृति पदानुक्रम की कम से कम मूल अवधारणाओं पर ध्यान देने योग्य है (विशेषकर पृष्ठ दोष और कैश मिस से संबंधित ऊपरी स्तर) ।

जावा माइक्रो-ऑप्टिमाइज़ेशन के लिए बहुत सारे कमरे छोड़ता है

क्षमा करें, क्षमा करें - उस तरह के शेख़ी के साथ:

क्या जेवीएम का "जादू" जावा में माइक्रो-ऑप्टिमाइज़ेशन पर एक प्रोग्रामर के प्रभाव को बाधित करता है?

थोड़ा सा लेकिन उतना नहीं जितना लोग सोच सकते हैं अगर आप इसे सही करते हैं। उदाहरण के लिए, यदि आप इमेज प्रोसेसिंग कर रहे हैं, तो मूल कोड में हस्तलिखित SIMD, मल्टीथ्रेडिंग, और मेमोरी ऑप्टिमाइजेशन (एक्सेस पैटर्न और संभवतः इमेज प्रोसेसिंग एल्गोरिदम के आधार पर प्रतिनिधित्व भी), यह 32 के लिए प्रति सेकंड लाखों पिक्सेल प्रति सेकंड क्रंच करना आसान है- बिट RGBA पिक्सेल (8-बिट कलर चैनल) और कभी-कभी प्रति सेकंड अरबों भी।

जावा में कहीं भी पास होना असंभव है यदि आप कहते हैं, तो एक Pixelवस्तु बनाई गई है (यह अकेले पिक्सेल का आकार 4 बाइट्स से 16 से 64-बिट पर बढ़ेगा)।

लेकिन यदि आप Pixelऑब्जेक्ट को टालते हैं, तो बाइट्स की एक सरणी का उपयोग करते हैं, और एक Imageऑब्जेक्ट को मॉडल करते समय आप बहुत करीब आ सकते हैं । यदि आप सादे पुराने डेटा के सरणियों का उपयोग करना शुरू करते हैं, तो जावा का अभी भी बहुत सक्षम है। मैंने जावा में पहले इस तरह की चीजों की कोशिश की है और काफी प्रभावित हुआ है बशर्ते कि आप हर जगह छोटी नन्ही वस्तुओं का एक गुच्छा न बनाएं जो सामान्य से 4 गुना बड़ा हो (उदा: intइसके बजाय का उपयोग करें Integer) और एक तरह से थोक इंटरफेस मॉडलिंग करना शुरू करें Imageइंटरफ़ेस, इंटरफ़ेस नहीं Pixel। मैं यह कहने के लिए भी उद्यम करूंगा कि यदि आप सादे पुराने डेटा पर लूपिंग कर रहे हैं तो जावा सी ++ परफॉर्मेंस को टक्कर दे सकता है, न कि ऑब्जेक्ट्स ( floatजैसे, नहीं Float) की विशाल सरणियों को ।

शायद स्मृति आकारों से भी अधिक महत्वपूर्ण यह है कि एक सरणी intएक सन्निहित प्रतिनिधित्व की गारंटी देती है। की एक सरणी Integerनहीं है। संदर्भ के स्थानीयता के लिए योगदान अक्सर आवश्यक होता है क्योंकि इसका मतलब है कि कई तत्व (उदा: 16 ints) सभी एक कैश लाइन में फिट हो सकते हैं और संभावित रूप से कुशल मेमोरी एक्सेस पैटर्न के साथ बेदखल होने से पहले एक साथ पहुंच सकते हैं। इस बीच एक Integerस्मृति आसपास की स्मृति में अप्रासंगिक होने के साथ कहीं-कहीं फंसी हो सकती है, केवल एक पूर्णांक का उपयोग करने के लिए केवल 16 पूर्णांकों का विरोध करने के लिए मेमोरी के उस क्षेत्र को कैश लाइन में लोड किया गया है। भले ही हम अद्भुत और भाग्यशाली होIntegersस्मृति में एक दूसरे के ठीक बगल में थे, हम केवल 4 को कैश लाइन में फिट कर सकते हैं जो कि Integer4 गुना बड़ा होने के परिणामस्वरूप बेदखल होने से पहले पहुँचा जा सकता है , और यह सबसे अच्छी स्थिति में है।

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

मैंने हाल ही में C ++ में पढ़ा है कभी-कभी डेटा सदस्यों का आदेश अनुकूलन प्रदान कर सकता है [...]

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

यदि नहीं, तो लोग उदाहरण दे सकते हैं कि आप जावा (सरल संकलक झंडे के अलावा) में किन चालों का उपयोग कर सकते हैं।

जावा और C ++ के बीच एक अनुकूलन मानसिकता के संदर्भ में मेरे लिए सबसे बड़ा अंतर यह है कि C ++ आपको प्रदर्शन-महत्वपूर्ण परिदृश्य में जावा की तुलना में वस्तुओं को थोड़ा (नन्हा) बिट का उपयोग करने की अनुमति दे सकता है। उदाहरण के लिए, C ++ एक पूर्णांक को एक वर्ग के साथ लपेट सकता है जिसमें कोई ओवरहेड नहीं है (सभी जगह बेंचमार्क किए गए)। जावा के पास मेटाडेटा पॉइंटर-स्टाइल + अलाइनमेंट पैडिंग ओवरहेड प्रति ऑब्जेक्ट है, यही कारण है कि Booleanइससे बड़ा है boolean(लेकिन बदले में प्रतिबिंब के समान लाभ प्रदान करता है और किसी भी फ़ंक्शन को ओवरराइड करने की क्षमता finalहर एक यूडीटी के लिए चिह्नित नहीं है )।

गैर-सजातीय क्षेत्रों में स्मृति लेआउट की समीपता को नियंत्रित करने के लिए C ++ में यह थोड़ा आसान है (उदाहरण के लिए: एक संरचना / वर्ग के माध्यम से एक सरणी में फ्लोट और इन्टस को समतल करना), क्योंकि स्थानिक स्थानीयता अक्सर खो जाती है (या कम से कम नियंत्रण खो जाता है) जावा में जब जीसी के माध्यम से वस्तुओं का आवंटन किया जाता है।

... लेकिन अक्सर उच्चतम-प्रदर्शन समाधान अक्सर उन लोगों को विभाजित करेंगे और सादे पुराने डेटा के सन्निहित सरणियों पर एक SoA पहुंच पैटर्न का उपयोग करेंगे। इसलिए जिन क्षेत्रों में चरम प्रदर्शन की आवश्यकता है, जावा और सी ++ के बीच मेमोरी लेआउट को अनुकूलित करने की रणनीति अक्सर एक ही होती है, और अक्सर आपने संग्रह-शैली के इंटरफेस के पक्ष में उन नन्हे ऑब्जेक्ट-उन्मुख इंटरफेस को ध्वस्त किया होगा जो गर्म / जैसी चीजें कर सकते हैं ठंडे क्षेत्र में बंटवारा, SoA प्रतिनिधि, आदि गैर-सजातीय AoSoA प्रतिनिधि जावा में असंभव की तरह प्रतीत होते हैं (जब तक कि आपने बाइट के कच्चे सरणी या ऐसा कुछ नहीं किया था), लेकिन वे दुर्लभ मामलों के लिए हैं जहां दोनोंअनुक्रमिक और यादृच्छिक अभिगम पैटर्न एक साथ गर्म क्षेत्रों के लिए फ़ील्ड प्रकारों का मिश्रण होने के दौरान तेज़ होने की आवश्यकता है। यदि आप चोटी के प्रदर्शन के लिए पहुंच रहे हैं, तो इन दोनों के बीच अनुकूलन रणनीति के अंतर के सामान्य स्तर पर (सामान्य स्तर पर) अंतर का थोक मूल्य है।

यदि आप केवल "अच्छे" प्रदर्शन के लिए पहुंच रहे हैं, तो मतभेद बहुत अधिक भिन्न होते हैं - Integerबनाम छोटी वस्तुओं के साथ ज्यादा कुछ करने में सक्षम नहीं intहोना चाहिए , जैसे कि पीआईटीए का थोड़ा अधिक हो सकता है, खासकर जिस तरह से यह जेनेरिक के साथ बातचीत करता है। । यह जावा में एक केंद्रीय अनुकूलन लक्ष्य के रूप में बस का निर्माण एक सामान्य डेटा संरचना करने के लिए थोड़ा मुश्किल है कि के लिए काम करता है int, floatआदि, जबकि उन बड़ा और महंगा UDTs से परहेज है, लेकिन अक्सर सबसे प्रदर्शन महत्वपूर्ण क्षेत्रों हाथ से रोलिंग अपनी खुद की डाटा संरचनाओं की आवश्यकता होगी वैसे भी एक बहुत ही विशिष्ट उद्देश्य के लिए तैयार है, इसलिए यह केवल उस कोड के लिए कष्टप्रद है जो अच्छे प्रदर्शन के लिए प्रयास कर रहा है लेकिन चोटी के प्रदर्शन के लिए नहीं।

ऑब्जेक्ट ओवरहेड

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

यदि किसी को इस भाग के बारे में संदेह है, तो मैं एक लाख यादृच्छिक intsबनाम एक लाख यादृच्छिक के बीच एक बेंचमार्क बनाने का सुझाव दूंगा Integersऔर इसे बार-बार करने के लिए ( Integersप्रारंभिक जीसी चक्र के बाद स्मृति में फेरबदल होगा)।

अल्टिमेट ट्रिक: इंटरफ़ेस डिज़ाइन जो ऑप्टिमाइज़ करने के लिए कमरे को छोड़ दें

तो परम जावा ट्रिक जैसा कि मैं इसे देखता हूं अगर आप एक ऐसी जगह से निपट रहे हैं जो छोटी वस्तुओं पर भारी भार संभालती है (उदाहरण: ए Pixel, 4-वेक्टर, 4x4 मैट्रिक्स, ए Particle, संभवत: यहां तक ​​कि Accountअगर यह केवल कुछ छोटी है फ़ील्ड) इन नन्हा चीजों के लिए वस्तुओं का उपयोग करने से बचने और सादे पुराने डेटा के सरणियों (संभवतः एक साथ जंजीर) का उपयोग करना है। तो संग्रह इंटरफेस बन वस्तुओं की तरह Image, ParticleSystem, Accounts,, व्यक्तिगत लोगों सूचकांक द्वारा पहुँचा जा सकता मैट्रिक्स या वैक्टर, आदि का एक संग्रह है, जैसे यह भी सी और सी में अंतिम डिजाइन चाल ++ में से एक है के बाद से भी है कि बुनियादी वस्तु भूमि के ऊपर के बिना और स्मृति से विमुख, एकल कण के स्तर पर इंटरफ़ेस को मॉडलिंग करना सबसे कुशल समाधानों को रोकता है।


1
यह देखते हुए कि थोक में खराब प्रदर्शन वास्तव में महत्वपूर्ण क्षेत्रों में चरम प्रदर्शन को बढ़ाने का एक अच्छा मौका हो सकता है, मुझे नहीं लगता कि कोई भी आसानी से अच्छा प्रदर्शन होने के लाभ की पूरी तरह से उपेक्षा कर सकता है। और एरेज के स्ट्रक्चर में अरेंज मैरिज को मोड़ने की ट्रिक कुछ हद तक तब टूट जाती है जब मूल स्ट्रक्चर्स में से सभी (या लगभग सभी) वैल्यू एक ही समय में एक्सेस हो जाएंगे। BTW: मैं देख रहा हूँ कि आप बहुत सारी पुरानी पोस्टों को अनसुना कर रहे हैं और अपने स्वयं के अच्छे उत्तर को जोड़ रहे हैं, कभी-कभी अच्छी
विश्वसनीयता भी;;

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

1
खैर, मैंने केवल इसलिए ध्यान दिया क्योंकि चीजें वास्तव में धीमी हैं। और मैंने अपना समय हर एक के साथ निकाला।
Deduplicator

मैं वास्तव में आश्चर्यचकित हूं कि क्यों user204677चले गए। इतना बढ़िया जवाब।
ऑलिगॉफ्रेन

3

माइक्रो-ऑप्टिमाइज़ेशन के बीच एक मध्य क्षेत्र है, एक तरफ और एल्गोरिदम का अच्छा विकल्प, दूसरी तरफ।

यह निरंतर-कारक स्पीडअप का क्षेत्र है, और यह परिमाण के आदेश प्राप्त कर सकता है।
जिस तरह से ऐसा होता है वह निष्पादन समय के पूरे अंशों को बंद करके होता है, जैसे पहले 30%, फिर जो बचा है उसका 20%, फिर उसका 50%, और कई पुनरावृत्तियों के लिए, जब तक कि शायद ही कुछ बचा हो।

आप इसे छोटे डेमो-शैली कार्यक्रमों में नहीं देखते हैं। जहाँ आप देखते हैं कि यह कई बड़े डेटा प्रोग्राम्स में है, जहाँ बहुत सारे लेयर स्टैक हैं, जहाँ आमतौर पर कॉल लेयर्स कई लेयर्स डीप हैं। स्पीडअप के अवसरों को खोजने का एक अच्छा तरीका प्रोग्राम के राज्य के यादृच्छिक-समय के नमूनों की जांच करना है

आम तौर पर स्पीडअप में चीजों का समावेश होता है जैसे:

  • newपुराने ऑब्जेक्ट्स को पूलिंग और री-यूज़ करके कॉल को कम करना ,

  • उन चीजों को पहचानना जो सामान्यता के लिए वहां की तरह हैं, वास्तव में आवश्यक होने के बजाय,

  • विभिन्न संग्रह कक्षाओं का उपयोग करके डेटा संरचना को संशोधित करना, जिसमें एक ही बड़ा-ओ व्यवहार होता है, लेकिन वास्तव में उपयोग किए जाने वाले एक्सेसिंग पैटर्न का लाभ उठाते हैं,

  • फ़ंक्शन को फिर से कॉल करने के बजाय फ़ंक्शन कॉल द्वारा प्राप्त किए गए डेटा की बचत, (यह प्रोग्रामर की एक स्वाभाविक और मनोरंजक प्रवृत्ति है यह मानने के लिए कि छोटे नाम तेजी से निष्पादित होते हैं।)

  • निरर्थक डेटा संरचनाओं के बीच असंगतता की एक निश्चित राशि को सहन करना, जैसा कि उन्हें अधिसूचना घटनाओं के साथ पूरी तरह से संगत रखने की कोशिश करने के लिए विरोध किया गया था,

  • आदि आदि।

लेकिन निश्चित रूप से इनमें से कोई भी काम पहले नहीं किया जाना चाहिए ताकि नमूने लेकर समस्याओं को दिखाया जा सके।


2

जावा (जहां तक ​​मुझे पता है) आपको मेमोरी में परिवर्तनशील स्थानों पर कोई नियंत्रण नहीं देता है, इसलिए आपके पास झूठी साझा करने और चर के संरेखण जैसी चीजों से बचने के लिए कठिन समय है (आप कई अप्रयुक्त सदस्यों के साथ एक वर्ग को बाहर कर सकते हैं)। एक और बात मुझे नहीं लगता कि आप इसका लाभ उठा सकते हैं जैसे कि निर्देश mmpause, लेकिन ये चीजें सीपीयू विशिष्ट हैं और इसलिए यदि आपको लगता है कि आपको इसकी आवश्यकता है तो जावा उपयोग करने के लिए भाषा नहीं हो सकती है।

वहाँ असुरक्षित वर्ग मौजूद है जो आपको C / C ++ का लचीलापन देता है लेकिन C / C ++ के खतरे के साथ भी।

यह आपको अपने कोड के लिए जेवीएम बनाने वाले असेंबली कोड को देखने में मदद कर सकता है

इस तरह के विवरण को देखने वाले एक जावा ऐप के बारे में पढ़ने के लिए LMAX द्वारा जारी डिस्प्रेटर कोड देखें


2

इस प्रश्न का उत्तर देना बहुत कठिन है, क्योंकि यह भाषा के कार्यान्वयन पर निर्भर करता है।

सामान्य तौर पर इन दिनों ऐसी "सूक्ष्म अनुकूलन" के लिए बहुत कम जगह है। मुख्य कारण यह है कि संकलन के दौरान कंपाइलर इस तरह के अनुकूलन का लाभ उठाते हैं। उदाहरण के लिए, पूर्व-वेतन वृद्धि और पोस्ट-इंक्रीमेंट ऑपरेटरों के बीच उन स्थितियों में कोई प्रदर्शन अंतर नहीं है जहां उनके शब्दार्थ समान हैं। एक अन्य उदाहरण उदाहरण के लिए एक लूप होगा, for(int i=0; i<vec.size(); i++)जहां कोई यह तर्क दे सकता है कि कॉल करने के बजायsize()प्रत्येक पुनरावृत्ति के दौरान सदस्य फ़ंक्शन लूप से पहले वेक्टर का आकार प्राप्त करना बेहतर होगा और फिर उस एकल चर के खिलाफ तुलना करना और इस प्रकार प्रति कॉल फ़ंक्शन को टालना बेहतर होगा। हालांकि, ऐसे मामले हैं जिनमें एक संकलक इस मूर्खतापूर्ण मामले का पता लगाएगा और परिणाम को कैश करेगा। हालांकि, यह केवल तभी संभव है जब फ़ंक्शन का कोई साइड-इफेक्ट न हो और कंपाइलर यह सुनिश्चित कर सके कि लूप के दौरान वेक्टर आकार स्थिर रहता है, इसलिए यह केवल काफी तुच्छ मामलों पर लागू होता है।


दूसरे मामले के लिए, मुझे नहीं लगता कि संकलक भविष्य में इसे अनुकूलित कर सकते हैं। यह पता लगाना कि वेज को ऑप्टिमाइज़ करना सुरक्षित है () यह साबित करने पर निर्भर करता है कि अगर वेक्टर / लॉस्ट लूप के अंदर नहीं बदलता है, तो आकार जो समस्या को हल करने के कारण अनिर्दिष्ट है।
रेयान

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

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

@ LieRyan यदि आप केवल constइस वेक्टर पर कॉल करते हैं, तो मुझे पूरा यकीन है कि कई ऑप्टिमाइज़िंग कंपाइलर इसका पता लगा लेंगे।
K.Steff

C # में, और मुझे लगता है कि मैं जावा में भी पढ़ता हूं, यदि आप संचयकर्ता को कैश नहीं करते हैं, तो यह जानता है कि यह चेक को हटा सकता है कि क्या आप सरणी सीमा के बाहर जा रहे हैं, और यदि आप कैशे का आकार करते हैं तो यह चेक करना होगा , जो आम तौर पर आप की तुलना में अधिक खर्च कर रहे हैं कैशिंग द्वारा बचत कर रहे हैं। ऑप्टिमाइज़ करने वाले ऑप्टिमाइज़र की कोशिश शायद ही कभी एक अच्छी योजना है।
केट ग्रेगोरी

1

क्या आप जावा (साधारण संकलक झंडे के अलावा) में किन चालों का उपयोग कर सकते हैं, इसका उदाहरण लोग दे सकते हैं।

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

जावा उदाहरण 1000x1000 ints की एक सरणी तक पहुँचने के लिए

नीचे दिए गए नमूना कोड पर विचार करें - यह मेमोरी के एक ही क्षेत्र (1000x1000 सरणी के किलों) तक पहुंचता है, लेकिन एक अलग क्रम में। पर मेरे मैक मिनी (कोर i7, 2.7 GHz) आउटपुट के रूप में है इस प्रकार है, दिखा रहा है कि सरणी से भी अधिक पंक्तियों से traversing युगल प्रदर्शन (100 राउंड प्रत्येक में औसत)।

Processing columns by rows*** took 4 ms (avg)
Processing rows by columns*** took 10 ms (avg) 

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

कोर i7 (रेतीले पुल) के लिए एक कैश लाइन 64 बाइट्स रखती है, इस प्रकार प्रत्येक मेमोरी एक्सेस 64 बाइट्स प्राप्त करता है। क्योंकि पहला परीक्षण एक अनुमानित अनुक्रम में मेमोरी तक पहुंचता है, इससे पहले कि प्रोग्राम वास्तव में खपत हो, इससे पहले प्रोसेसर डेटा को प्री-भ्रूण करेगा। कुल मिलाकर, यह मेमोरी एक्सेस पर कम विलंबता का परिणाम है और इस प्रकार प्रदर्शन को बेहतर बनाता है।

नमूना कोड:

  package test;

  import java.lang.*;

  public class PerfTest {
    public static void main(String[] args) {
      int[][] numbers = new int[1000][1000];
      long startTime;
      long stopTime;
      long elapsedAvg;
      int tries;
      int maxTries = 100;

      // process columns by rows 
      System.out.print("Processing columns by rows");
      for(tries = 0, elapsedAvg = 0; tries < maxTries; tries++) {
       startTime = System.currentTimeMillis();
       for(int r = 0; r < 1000; r++) {
         for(int c = 0; c < 1000; c++) {
           int v = numbers[r][c]; 
         }
       }
       stopTime = System.currentTimeMillis();
       elapsedAvg += ((stopTime - startTime) - elapsedAvg) / (tries + 1);
      }

      System.out.format("*** took %d ms (avg)\n", elapsedAvg);     

      // process rows by columns
      System.out.print("Processing rows by columns");
      for(tries = 0, elapsedAvg = 0; tries < maxTries; tries++) {
       startTime = System.currentTimeMillis();
       for(int c = 0; c < 1000; c++) {
         for(int r = 0; r < 1000; r++) {
           int v = numbers[r][c]; 
         }
       }
       stopTime = System.currentTimeMillis();
       elapsedAvg += ((stopTime - startTime) - elapsedAvg) / (tries + 1);
      }

      System.out.format("*** took %d ms (avg)\n", elapsedAvg);     
    }
  }

1

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

एक विघटनकारी लेखकों के विषय पर एक अत्यधिक जानकारीपूर्ण ब्लॉग को पढ़ने की सिफारिश की जाती है:

एक को हमेशा यह पूछना पड़ता है कि यदि आप माइक्रो-ऑप्टिमाइज़ेशन चाहते हैं तो जावा का उपयोग करने में क्यों परेशान होते हैं, तो किसी फ़ंक्शन के त्वरण के लिए कई वैकल्पिक तरीके हैं जैसे कि जेएनए या जेएनआई का उपयोग करके एक देशी पुस्तकालय में पास होना।

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