2015-10-12 4 views
6

हास्केल में ऐसा कुछ क्यों धीमा चलता है?हास्केल में [x | x <- [1..10]] विधि इतनी धीमी क्यों है?

test = [x|a<-[1..100],b<-[1..100],c<-[1..100],d<-[1..100],let x = a] 

print $ length test 

चलाने के लिए केवल के बारे में 10^8 संख्या नहीं है, यह आँख की झपकी में किया जाना चाहिए, लेकिन यह हमेशा के लिए चल रहा है की तरह लगता है और लगभग दुर्घटनाग्रस्त हो गया।

+9

यह "क्यों" प्रश्न का उत्तर नहीं देता है, लेकिन: अनुकूलन के साथ संकलित करें। मैं ghci में 25s का पालन करें; 20-एस 'के साथ संकलित जब; लेकिन '-O2' के साथ संकलित जब केवल 0.3s। –

+1

अंतिम उत्तर 10^8 नहीं 100 क्यों है क्योंकि एक असाइनमेंट है 'चलो x = a'? – Kamel

+0

@ कमल 'चलो x = a' सभी बाइंड्स के बाद है, इसलिए 'x' को अभी भी 10^8 बार दोहराया जाना चाहिए। –

उत्तर

5

क्या आप इसे ghci में या संकलित प्रोग्राम में चला रहे हैं? इससे बड़ा फर्क पड़ता है।

यदि ghci में है, तो ghci बाद में इसका उपयोग करना चाहते हैं तो test के गणना मूल्य को बनाए रखेगा। आम तौर पर यह एक अच्छा विचार है, लेकिन इस मामले में जहां test एक बड़ा मूल्य है जो किसी भी तरह से पुन: सम्मिलित करने के लिए सस्ता होगा। कितना बड़ा स्टार्टर्स के लिए यह 10^8 तत्वों की सूची है, और (64-बिट सिस्टम पर) एक सूची में प्रति तत्व 24 बाइट्स खर्च होते हैं, इसलिए यह पहले से ही 2.4 जी है। फिर मूल्यों का अंतरिक्ष उपयोग स्वयं होता है। कोई सोच सकता है कि मूल्य [1..100] से लिया गया है, इसलिए उन्हें साझा किया जाना चाहिए और कुल में नगण्य राशि का उपयोग करना चाहिए। लेकिन सूची में मूल्य वास्तव में x के रूप में हैं, जो a, b, c और d पर निर्भर हो सकता है, और length सूची में मूल्यों की जांच कभी नहीं करता है क्योंकि यह इसे पार करता है। इसलिए प्रत्येक तत्व को बंद करने के रूप में दर्शाया जा रहा है जो a, b, c और d को संदर्भित करता है, जो कम से कम 8 * (4 + 1) = 40 और बाइट्स लेता है, जो हमें कुल 6.4 जी तक ले जाता है।

यह बहुत कुछ है, और जब आप डेटा के 6.4 जी आवंटित करते हैं तो कचरा कलेक्टर को बहुत सारी प्रतिलिपि करना पड़ता है, यह सब स्थायी रूप से रहता है। यह इतना लंबा लगता है, वास्तव में सूची या इसकी लंबाई की गणना नहीं करता है।

आप इस कार्यक्रम

test = [x|a<-[1..100],b<-[1..100],c<-[1..100],d<-[1..100],let x = a] 

main = print $ length test 

तो test के रूप में अपनी लंबाई की गणना की जा रही है लाइव रखा जाना नहीं है, के रूप में स्पष्ट रूप से यह कभी नहीं फिर से इस्तेमाल किया जा रहा है संकलन है। तो अब जीसी के पास लगभग कोई काम नहीं है, और कार्यक्रम कुछ सेकंड में चलता है (Integer पर ~ 10^8 सूची नोड आवंटन और गणना के लिए उचित)।

4

आप केवल 10^8 बार लूप नहीं चला रहे हैं, आप 10^8 तत्वों के साथ एक सूची बना रहे हैं। चूंकि आप length का उपयोग कर रहे हैं, इसलिए हास्केल को वास्तव में अपनी लंबाई को वापस करने के लिए पूरी सूची का मूल्यांकन करना होगा। सूची में प्रत्येक तत्व एक शब्द लेता है, जो 32 बिट्स हो सकता है या 64 बिट हो सकता है। आशावादी धारणा पर कि यह 32 बिट्स (4 बाइट्स) है, आपने अभी 400 एमबी (लगभग 381.5 एमआईबी) स्मृति आवंटित की है। यदि यह 64 बिट्स है, तो यह 800 एमबी (लगभग 763 एमआईबी) मेमोरी है जिसे आपने अभी आवंटित किया है। आपके सिस्टम पर और क्या चल रहा है, इस पर निर्भर करते हुए, हो सकता है कि आपने उस रैंक पर उस रैम को आवंटित करके स्वैप फ़ाइल/स्वैप विभाजन को दबाया हो।

यदि अन्य सूक्ष्मताएं चल रही हैं, तो मुझे उनके बारे में पता नहीं है, लेकिन स्मृति उपयोग मेरा पहला संदेह है कि यह इतना धीमा क्यों है।

+4

आप शायद इस स्मृति के लिए आवंटित स्मृति की कुल मात्रा को कम करके आंका रहे हैं। लेकिन मुझे सच में नहीं लगता कि यह समस्या है: 1 जीबी आपको किसी भी आधुनिक प्रणाली पर स्वैप करने के लिए मजबूर नहीं करेगा; और फिर भी, हमें किसी भी समय स्मृति में अधिकतम 10^6 तत्वों को रखने की आवश्यकता होनी चाहिए। जीएचसी का आवंटन नाश्ते के लिए दस लाख आवंटन खाएगा; तब तक प्रतीक्षा करें जब तक कि आप देखते हैं कि आवंटन/डीलोकेशन के कितने गीगाबाइट यहां तक ​​कि सरल, शॉर्ट-रनिंग प्रोग्राम के प्रोफाइलिंग आउटपुट में दिखाई देते हैं। –

+0

कोई संदेह नहीं; मैं हास्केल विशेषज्ञ नहीं हूं (मुझे एफ # के बारे में और पता है, और यहां तक ​​कि मैं अभी तक केवल डबलिंग कर रहा हूं)। @ सीवाईसी, अगर डैनियल वाग्नेर कुछ भी मुझसे विरोधाभास करता है, तो उसे सुनो, मुझे नहीं। मैं ज्यादातर अनुमान लगा रहा हूँ। – rmunn

+0

@DanielWagner किसी भी समय किसी भी समय स्मृति में 10^6 तत्वों को रखने की आवश्यकता क्यों होगी? – immibis

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