आलस्य
यह "कंपाइलर ऑप्टिमाइज़ेशन" नहीं है, लेकिन यह भाषा विनिर्देश द्वारा गारंटीकृत कुछ है, इसलिए आप हमेशा ऐसा होने पर भरोसा कर सकते हैं। अनिवार्य रूप से, इसका मतलब है कि जब तक आप परिणाम के साथ "कुछ नहीं" करते हैं, तब तक कार्य नहीं किया जाता है। (जब तक आप जानबूझकर आलस्य को बंद करने के लिए कई चीजों में से एक करते हैं।)
यह, जाहिर है, अपने आप में एक संपूर्ण विषय है, और एसओ के पास इसके बारे में बहुत सारे सवाल और जवाब हैं।
मेरे सीमित अनुभव में, आपके कोड को बहुत आलसी या बहुत सख्त बनाने से मेरे द्वारा बात की जाने वाली किसी भी अन्य सामग्री की तुलना में बहुत बड़ा प्रदर्शन दंड (समय और स्थान में) होता है ...
कठोरता का विश्लेषण
आलस्य काम से बचने के बारे में है जब तक कि यह आवश्यक न हो। यदि संकलक यह निर्धारित कर सकता है कि एक दिया गया परिणाम "हमेशा" की आवश्यकता होगी, तो यह गणना को संग्रहीत करने और बाद में प्रदर्शन करने से परेशान नहीं करेगा; यह सिर्फ इसे सीधे प्रदर्शन करेगा, क्योंकि यह अधिक कुशल है। यह तथाकथित "कठोरता विश्लेषण" है।
गेटा, जाहिर है, संकलक हमेशा नहीं कर सकता है पता लगा कब कुछ सख्त किया जा सकता है। कभी-कभी आपको संकलक को थोड़ा संकेत देने की आवश्यकता होती है। (मैं यह निर्धारित करने के लिए किसी भी आसान तरीके से अवगत नहीं हूं कि क्या कठोरता विश्लेषण ने आपको लगता है कि यह क्या है, मुख्य आउटपुट के माध्यम से वैडिंग के अलावा है।)
इनलाइन
यदि आप किसी फ़ंक्शन को कॉल करते हैं, और कंपाइलर यह बता सकता है कि आप किस फ़ंक्शन को कॉल कर रहे हैं, तो यह फ़ंक्शन को "इनलाइन" करने का प्रयास कर सकता है - अर्थात, फ़ंक्शन कॉल को फ़ंक्शन की एक प्रति के साथ बदलने के लिए। एक फ़ंक्शन कॉल का ओवरहेड आमतौर पर बहुत छोटा होता है, लेकिन इनलाइनिंग अक्सर अन्य अनुकूलन करने में सक्षम होती है जो अन्यथा नहीं हुई होती, इसलिए इनलाइनिंग एक बड़ी जीत हो सकती है।
फ़ंक्शंस केवल इनबिल्ड हैं यदि वे "छोटे पर्याप्त" हैं (या यदि आप विशेष रूप से इनलाइनिंग के लिए एक प्रागम जोड़ते हैं)। इसके अलावा, फ़ंक्शन केवल इनबिल्ड हो सकते हैं यदि कंपाइलर बता सकता है कि आप किस फ़ंक्शन को कॉल कर रहे हैं। दो मुख्य तरीके हैं जो संकलक बताने में असमर्थ हो सकते हैं:
यदि आप जिस फ़ंक्शन को कॉल कर रहे हैं वह कहीं और से पारित किया गया है उदाहरण के लिए, जब filter
फ़ंक्शन संकलित किया जाता है, तो आप फ़िल्टर को विधेय को इनलाइन नहीं कर सकते, क्योंकि यह एक उपयोगकर्ता द्वारा दिया गया तर्क है।
यदि आप जिस फ़ंक्शन को कॉल कर रहे हैं वह एक क्लास विधि है और कंपाइलर को पता नहीं है कि क्या प्रकार शामिल है। जैसे, जब sum
फ़ंक्शन संकलित किया जाता है, तो कंपाइलर +
फ़ंक्शन को इनलाइन नहीं कर सकता है , क्योंकि sum
कई अलग-अलग प्रकारों के साथ काम करता है, जिनमें से प्रत्येक का एक अलग +
फ़ंक्शन होता है।
बाद के मामले में, आप {-# SPECIALIZE #-}
किसी विशेष प्रकार के हार्ड-कोडेड फ़ंक्शन के संस्करणों को उत्पन्न करने के लिए प्रज्ञा का उपयोग कर सकते हैं । उदाहरण के लिए, हार्ड-कोडित {-# SPECIALIZE sum :: [Int] -> Int #-}
का एक संस्करण संकलित करेगा , जिसका अर्थ है कि इस संस्करण में इनलेट किया जा सकता है।sum
Int
+
ध्यान दें, हालांकि, हमारा नया विशेष sum
कार्य केवल तभी कहा जाएगा जब कंपाइलर बता सकता है कि हम साथ काम कर रहे हैं Int
। अन्यथा मूल, बहुरूपी sum
कहलाता है। फिर, वास्तविक फ़ंक्शन कॉल ओवरहेड काफी छोटा है। यह अतिरिक्त अनुकूलन है जो इनलाइनिंग को सक्षम कर सकता है जो लाभकारी हैं।
सामान्य उपसर्ग उन्मूलन
यदि कोड का एक निश्चित ब्लॉक दो बार समान मूल्य की गणना करता है, तो कंपाइलर उसी गणना के एकल उदाहरण के साथ प्रतिस्थापित कर सकता है। उदाहरण के लिए, यदि आप करते हैं
(sum xs + 1) / (sum xs + 2)
तब कंपाइलर इसे ऑप्टिमाइज़ कर सकता है
let s = sum xs in (s+1)/(s+2)
आप उम्मीद कर सकते हैं कि कंपाइलर हमेशा ऐसा करेगा । हालांकि, जाहिरा तौर पर कुछ स्थितियों में इसका परिणाम खराब प्रदर्शन हो सकता है, बेहतर नहीं, इसलिए जीएचसी हमेशा ऐसा नहीं करता है । सच कहूँ तो, मैं वास्तव में इस एक के पीछे के विवरण को नहीं समझता। लेकिन नीचे की रेखा है, अगर यह परिवर्तन आपके लिए महत्वपूर्ण है, तो इसे मैन्युअल रूप से करना मुश्किल नहीं है। (और अगर यह महत्वपूर्ण नहीं है, तो आप इसके बारे में चिंता क्यों कर रहे हैं?)
केस के भाव
निम्नलिखित को धयान मे रखते हुए:
foo (0:_ ) = "zero"
foo (1:_ ) = "one"
foo (_:xs) = foo xs
foo ( []) = "end"
पहले तीन समीकरण सभी जाँचते हैं कि क्या सूची गैर-रिक्त है (अन्य चीजों के बीच)। लेकिन एक ही चीज को तीन बार चेक करना बेकार है। सौभाग्य से, यह संकलक के लिए बहुत से नेस्टेड केस अभिव्यक्तियों में इसे अनुकूलित करना बहुत आसान है। इस मामले में, कुछ इस तरह
foo xs =
case xs of
y:ys ->
case y of
0 -> "zero"
1 -> "one"
_ -> foo ys
[] -> "end"
यह कम सहज है, लेकिन अधिक कुशल है। क्योंकि संकलक इस परिवर्तन को आसानी से कर सकता है, आपको इसके बारे में चिंता करने की आवश्यकता नहीं है। बस संभव सबसे सहज तरीके से अपने पैटर्न मिलान लिखें; कंपाइलर इसे रीक्रिएट करने में बहुत अच्छा है और इसे जितना जल्दी संभव हो सके उतना फिर से व्यवस्थित करने के लिए।
विलय
सूची प्रसंस्करण के लिए मानक हास्केल मुहावरा एक साथ कार्य करने वाली श्रृंखला है जो एक सूची लेती है और एक नई सूची तैयार करती है। विहित उदाहरण
map g . map f
दुर्भाग्य से, जबकि आलसी आवश्यक काम लंघन की गारंटी देता है, मध्यवर्ती सूची एसएपी प्रदर्शन के लिए सभी आवंटन और सौदे। "फ्यूजन" या "वनों की कटाई" वह जगह है जहां संकलक इन मध्यवर्ती चरणों को खत्म करने की कोशिश करता है।
परेशानी यह है कि इनमें से अधिकांश कार्य पुनरावर्ती हैं। पुनरावृत्ति के बिना, यह सभी कार्यों को एक बड़े कोड ब्लॉक में स्क्वैश करने के लिए इनलाइन करने के लिए एक प्रारंभिक अभ्यास होगा, इस पर सरलीकृत चलाएं और कोई मध्यवर्ती सूचियों के साथ वास्तव में इष्टतम कोड का उत्पादन करें। लेकिन पुनरावृत्ति के कारण, यह काम नहीं करेगा।
{-# RULE #-}
इसमें से कुछ को ठीक करने के लिए आप प्रैग्मस का उपयोग कर सकते हैं । उदाहरण के लिए,
{-# RULES "map/map" forall f g xs. map f (map g xs) = map (f.g) xs #-}
अब हर बार जीएचसी map
लागू होता हैmap
, तो यह सूची में एक ही पास में अंतरिम सूची को समाप्त कर देता है।
परेशानी है, यह केवल map
उसके बाद के लिए काम करता है map
। कई अन्य संभावनाएं हैं - map
के बाद filter
, filter
जिसके बादmap
, आदि हाथ से कोड के बजाय उनमें से प्रत्येक के लिए एक समाधान, तथाकथित "स्ट्रीम फ्यूजन" का आविष्कार किया गया था। यह एक अधिक जटिल चाल है, जिसका मैं यहां वर्णन नहीं करूंगा।
इसका लंबा और छोटा होना: ये प्रोग्रामर द्वारा लिखे गए सभी विशेष अनुकूलन ट्रिक्स हैं । जीएचसी ही फ्यूजन के बारे में कुछ नहीं जानता है; यह सभी सूची पुस्तकालय और अन्य कंटेनर पुस्तकालयों में है। तो क्या अनुकूलन होता है यह इस बात पर निर्भर करता है कि आपके कंटेनर पुस्तकालयों को कैसे लिखा जाता है (या, अधिक वास्तविक रूप से, जो पुस्तकालय आप उपयोग करना चाहते हैं)।
उदाहरण के लिए, यदि आप हास्केल '98 सरणियों के साथ काम करते हैं, तो किसी भी प्रकार के संलयन की उम्मीद न करें। लेकिन मैं समझता हूं कि vector
पुस्तकालय में व्यापक संलयन क्षमताएं हैं। यह सभी पुस्तकालयों के बारे में है; संकलक सिर्फ RULES
प्रज्ञा प्रदान करता है । (जो बेहद शक्तिशाली है, वैसे। एक पुस्तकालय लेखक के रूप में, आप इसका उपयोग ग्राहक कोड को फिर से लिखने के लिए कर सकते हैं!)
मेटा:
मैं "कोड पहले, प्रोफ़ाइल दूसरे, तीसरे का अनुकूलन" कहने वाले लोगों से सहमत हूं।
मैं यह कहते हुए भी लोगों से सहमत हूं कि "किसी दिए गए डिज़ाइन के निर्णय की लागत कितनी है, इसके लिए एक मानसिक मॉडल होना उपयोगी है"।
सभी चीजों में संतुलन, और वह सब ...