2012-06-07 8 views
13

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

क्या इस विषय पर कोई दिलचस्प काम किया गया है? या यह एक शोध विफलता है या कुछ रोकथाम समस्या है जो हल करना बहुत मुश्किल है?

+1

आम तौर पर, एक ExecutorService या कांटा/में शामिल होने के लिए एक बेहतर विकल्प है। थ्रेड के बीच कार्यों को वितरित करने में लगने वाले समय को देखते हुए, यह बहुत ही असंभव है कि एक स्वचालित कंपाइलर हाथ लिखित कोड से बेहतर होगा। अक्सर साधारण लूप के लिए, एकाधिक धागे का उपयोग धीमा है। –

+0

अन्य कई कंपाइलर्स इन दिनों इस तरह की चीज करते हैं, मुझे कोई कारण नहीं दिखता कि एक जेवीएम/जेआईटी क्यों नहीं होना चाहिए। –

+0

मेरे व्यावहारिक अनुभव से, जेवीएम वास्तव में इस पर बहुत अच्छा है। – dagnelies

उत्तर

14

मुझे लगता है कि 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 बातें की पारदर्शी है:

  1. समारोह #(* 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! 
+0

यह पूरी तरह से अंकगणित गणनाओं के (अपेक्षाकृत सामान्य) मामले को पूरी तरह से अनदेखा करता है, जो हॉटस्पॉट को समानांतर करने के साथ-साथ किसी और को भी सक्षम करने में सक्षम होना चाहिए। यह कर सकते हैं? –

+0

@ लुइस: "पूरी तरह से अंकगणित गणना" से आपका क्या मतलब है? मुझे नहीं पता कि हॉटस्पॉट यह करता है, लेकिन सभी आधुनिक प्रोसेसर पहले से ही [शाखा भविष्यवाणी] कर रहे हैं (http://en.wikipedia.org/wiki/Branch_predictor) और [सट्टा अनुकूलन] (http: //en.wikipedia। संगठन/विकी/speculative_execution) तो मुझे यकीन नहीं है कि हॉटस्पॉट उसमें कुछ भी जोड़ सकता है या नहीं। –

+0

'महंगी कॉम्प्यूटेशन' विधियों को कॉल करने के बजाय, लूप के बारे में क्या है जो सीधे सरणी पर गणित करते हैं? –

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