मुझे लगता है कि Java memory model की वर्तमान गारंटी कंपेलर या वीएम स्तर पर स्वचालित समांतरता, यदि कोई हो, तो इसे करने में काफी मुश्किल होती है। जावा भाषा में यह गारंटी देने के लिए कोई अर्थशास्त्र नहीं है कि कोई भी डेटा संरचना प्रभावी रूप से अपरिवर्तनीय है, या कोई विशेष विवरण शुद्ध और साइड इफेक्ट्स से मुक्त है, इसलिए संकलक को समानांतर करने के लिए इन्हें स्वचालित रूप से समझना होगा। संकलक में अनुमान लगाने के लिए कुछ प्राथमिक अवसर संभव होंगे, लेकिन सामान्य मामला रनटाइम पर छोड़ा जाएगा, क्योंकि गतिशील लोडिंग और बाइंडिंग नए उत्परिवर्तनों को पेश कर सकती है जो संकलन-समय पर मौजूद नहीं थीं।
for (int i = 0; i < array.length; i++) {
array[i] = expensiveComputation(array[i]);
}
यह parallelize करने तुच्छ होगा, अगर expensiveComputation
एक pure function, जिसका उत्पादन केवल अपने तर्क पर निर्भर करता है, और अगर हम गारंटी दे सकें कि array
दौरान बदला नहीं जा होगा:
निम्नलिखित कोड पर विचार करें लूप (असल में हम इसे बदल रहे हैं, array[i]=...
सेट करते हैं, लेकिन इस विशेष मामले में expensiveComputation(array[i])
हमेशा पहले कहा जाता है, इसलिए यह ठीक है - यह मानते हुए कि array
स्थानीय है और कहीं और से संदर्भित नहीं है)।
इसके अलावा, अगर हम इस तरह पाश बदलने के लिए:
for (int i = 0; i < array.length; i++) {
array[i] = expensiveComputation(array, i);
// expensiveComputation has the whole array at its disposal!
// It could read or write values anywhere in it!
}
तो साथ में चलाना नहीं तुच्छ किसी भी अधिक है, भले ही expensiveComputation
शुद्ध है और इसके तर्क में परिवर्तन नहीं करता है, क्योंकि समानांतर धागे बदलते किया जाएगा array
की सामग्री जबकि अन्य इसे पढ़ रहे हैं! समांतरता को पता लगाना होगा कि सरणी expensiveComputation
के विभिन्न हिस्सों के तहत संदर्भित है और तदनुसार सिंक्रनाइज़ कर रहा है।
शायद यह एकमुश्त असंभव सब उत्परिवर्तन और साइड इफेक्ट है कि गलत हो रहा है पता लगाने और जब parallelizing खाते में उन लेने के लिए नहीं होगा, लेकिन यह होगा बहुत कठिन, सुनिश्चित करें के लिए, शायद में अव्यवहार्य अभ्यास। यही कारण है कि समांतरता, और यह पता लगाना कि सबकुछ अभी भी सही तरीके से काम करता है, जावा में प्रोग्रामर का सिरदर्द है।
कार्यात्मक भाषाएं (उदाहरण के लिए JVM पर क्लोजर) इस विषय का एक गर्म उत्तर है। persistent ("प्रभावी रूप से अपरिवर्तनीय") संरचनाओं के साथ शुद्ध, साइड-इफेक्ट-फ्री फ़ंक्शन संभावित रूप से अंतर्निहित या लगभग अंतर्निहित समांतरता की अनुमति देते हैं। के एक सरणी के प्रत्येक तत्व को दोगुना करते हैं:
(map #(* 2 %) [1 2 3 4 5])
(pmap #(* 2 %) [1 2 3 4 5]) ; The same thing, done in parallel.
इसका कारण यह है 2 बातें की पारदर्शी है:
- समारोह
#(* 2 %)
शुद्ध है: में कोई मान लेता है और एक मूल्य के बाहर देता है, और बस इतना ही। यह कुछ भी नहीं बदलता है, और इसका उत्पादन केवल इसके तर्क पर निर्भर करता है।
- वेक्टर
[1 2 3 4 5]
अपरिवर्तनीय है: कोई फर्क नहीं पड़ता कि यह किस पर देख रहा है, या जब यह वही है।
जावा में शुद्ध कार्य करना संभव है, लेकिन 2), अपरिवर्तनीयता, यहां एचिल्स की एड़ी है। जावा में कोई अपरिवर्तनीय सरणी नहीं है। पेडेंट होने के लिए, कुछ भी नहीं जावा में अपरिवर्तनीय है क्योंकि final
फ़ील्ड प्रतिबिंब का उपयोग करके बदला जा सकता है। इसलिए कोई गारंटी नहीं दी जा सकती है कि गणना के आउटपुट (या इनपुट!) को समांतरता से बदला नहीं जाएगा -> इसलिए स्वचालित समांतरता आम तौर पर अक्षम होती है।
गूंगा अचल स्थिति को "तत्वों को दोगुना" उदाहरण के मनमाने ढंग से जटिल प्रसंस्करण तक फैली हुई है, धन्यवाद:
(defn expensivefunction [v x]
(/ (reduce * v) x))
(let [v [1 2 3 4 5]]
(map (partial expensivefunction v) v)) ; pmap would work equally well here!
आम तौर पर, एक ExecutorService या कांटा/में शामिल होने के लिए एक बेहतर विकल्प है। थ्रेड के बीच कार्यों को वितरित करने में लगने वाले समय को देखते हुए, यह बहुत ही असंभव है कि एक स्वचालित कंपाइलर हाथ लिखित कोड से बेहतर होगा। अक्सर साधारण लूप के लिए, एकाधिक धागे का उपयोग धीमा है। –
अन्य कई कंपाइलर्स इन दिनों इस तरह की चीज करते हैं, मुझे कोई कारण नहीं दिखता कि एक जेवीएम/जेआईटी क्यों नहीं होना चाहिए। –
मेरे व्यावहारिक अनुभव से, जेवीएम वास्तव में इस पर बहुत अच्छा है। – dagnelies