performance - हास्केल(GHC) इतना तेज़ क्यों है?




haskell higher-order-functions (2)

खैर, यहाँ पर टिप्पणी करने के लिए बहुत कुछ है। मैं जितना हो सके उतना जवाब देने की कोशिश करूंगा।

सही तरीके से इस्तेमाल होने पर, यह निम्न-स्तर की भाषाओं के करीब-ईश प्राप्त कर सकता है।

मेरे अनुभव में, आमतौर पर 2x के भीतर कई मामलों में जंग के प्रदर्शन को प्राप्त करना संभव है। लेकिन कुछ (व्यापक) उपयोग के मामले भी हैं जहां प्रदर्शन निम्न-स्तरीय भाषाओं की तुलना में खराब है।

या इसे भी हराया, लेकिन इसका मतलब है कि आप एक अक्षम C कार्यक्रम का उपयोग कर रहे हैं, क्योंकि GHC Haskell to C को संकलित करता है)

जो पूरी तरह सही नहीं है। हास्केल C-- (C का सबसेट) के लिए संकलित करता है, जिसे बाद में मूल कोड जनरेटर के माध्यम से विधानसभा में संकलित किया जाता है। मूल कोड जनरेटर आमतौर पर सी कंपाइलर की तुलना में तेजी से कोड उत्पन्न करता है, क्योंकि यह कुछ अनुकूलन कर सकता है जो एक साधारण सी कंपाइलर नहीं कर सकता है।

मशीन आर्किटेक्चर स्पष्ट रूप से अनिवार्य हैं, जो कि ट्यूरिंग मशीनों पर आधारित हैं, मोटे तौर पर।

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

वास्तव में, हास्केल के पास एक विशिष्ट मूल्यांकन क्रम भी नहीं है।

दरअसल, हास्केल एक मूल्यांकन आदेश को स्पष्ट रूप से परिभाषित करते हैं।

इसके अलावा, मशीन डेटा प्रकारों से निपटने के बजाय, आप हर समय बीजीय डेटा प्रकार बनाते हैं।

वे कई मामलों में मेल खाते हैं, बशर्ते आपके पास पर्याप्त उन्नत संकलक हों।

आप सोचते होंगे कि मक्खी पर कार्य करना, और उन्हें चारों ओर फेंकना, एक कार्यक्रम को धीमा कर देगा।

हास्केल संकलित है, और इसलिए उच्च-क्रम के कार्य वास्तव में मक्खी पर नहीं बने हैं।

यह हास्केल कोड को अनुकूलित करने के लिए लगता है, आपको इसे और अधिक मशीन की बजाय अधिक सुरुचिपूर्ण और सार बनाने की आवश्यकता है।

सामान्य तौर पर, हास्केल में बेहतर प्रदर्शन पाने के लिए कोड को अधिक "मशीन जैसा" बनाना एक अनुत्पादक तरीका है। लेकिन इसे अधिक अमूर्त बनाना हमेशा एक अच्छा विचार नहीं है। एक अच्छा विचार सामान्य डेटा संरचनाओं और कार्यों का उपयोग कर रहा है जो कि भारी अनुकूलित किए गए हैं (जैसे लिंक की गई सूचियाँ)।

fx = [x] और f = pure उदाहरण के लिए, हास्केल में सटीक एक ही चीज़ है। एक अच्छा संकलक पूर्व मामले में बेहतर प्रदर्शन नहीं करेगा।

हास्केल (जीएचसी के साथ संकलित) इतना तेज क्यों है, इसकी अमूर्त प्रकृति और भौतिक मशीनों से अंतर को देखते हुए?

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

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

क्या असमंजस की बात यह है कि परिवर्तनशीलता धीमी कोड की ओर ले जाए? हास्केल के आलस्य का वास्तव में मतलब है कि उत्परिवर्तन उतना मायने नहीं रखता जितना आपको लगता है कि यह होगा, साथ ही यह उच्च-स्तर है इसलिए कई अनुकूलन हैं जो कंपाइलर लागू कर सकते हैं। इस प्रकार, रिकॉर्ड को संशोधित करने की जगह शायद ही कभी धीमी होगी क्योंकि यह सी जैसी भाषा में होगा।

हास्केल ( GHC संकलक के साथ) आपकी अपेक्षा से बहुत अधिक तेज है । सही तरीके से इस्तेमाल होने पर, यह निम्न-स्तर की भाषाओं के करीब-ईश प्राप्त कर सकता है। (Haskellers के लिए एक पसंदीदा बात यह है कि C के 5% के भीतर प्रयास करें और प्राप्त करें (या इसे हरा भी दें, लेकिन इसका मतलब है कि आप एक अक्षम C प्रोग्राम का उपयोग कर रहे हैं, क्योंकि GHC Haskell को C से संकलित करता है)।) मेरा सवाल है, क्यों?

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

सभी के अजीब हालांकि उच्च कार्य कार्य है। आप सोचते होंगे कि मक्खी पर कार्य करना, और उन्हें चारों ओर फेंकना, एक कार्यक्रम को धीमा कर देगा। लेकिन उच्च क्रम के कार्यों का उपयोग वास्तव में हास्केल को तेज बनाता है। वास्तव में, ऐसा लगता है कि, हास्केल कोड को अनुकूलित करने के लिए, आपको अधिक मशीन की तरह इसे और अधिक सुरुचिपूर्ण और सार बनाने की आवश्यकता है। हास्केल की अधिक उन्नत विशेषताओं में से कोई भी इसके प्रदर्शन को प्रभावित नहीं करता है, अगर वे इसमें सुधार नहीं करते हैं।

क्षमा करें यदि यह भयंकर लग रहा है, लेकिन यहां मेरा सवाल है: हास्केल (जीएचसी के साथ संकलित) इतनी तेज क्यों है, इसकी अमूर्त प्रकृति और भौतिक मशीनों से अंतर को देखते हुए?

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


मुझे लगता है कि यह एक छोटा सा राय-आधारित है। लेकिन मैं जवाब देने की कोशिश करूंगा।

मैं डिट्रिच एप के साथ सहमत हूं: यह कई चीजों का एक संयोजन है जो जीएचसी को तेज बनाते हैं।

और सबसे पहले, हास्केल बहुत उच्च-स्तरीय है। यह संकलक को आपके कोड को तोड़ने के बिना आक्रामक अनुकूलन करने में सक्षम बनाता है।

SQL के बारे में सोचो। अब, जब मैं एक SELECT स्टेटमेंट लिखता हूं, तो यह एक अनिवार्य लूप की तरह लग सकता है, लेकिन ऐसा नहीं है । ऐसा लग सकता है कि यह उस तालिका की सभी पंक्तियों पर लूप करता है जो निर्दिष्ट शर्तों से मेल खाने वाले को खोजने की कोशिश कर रहा है, लेकिन वास्तव में "कंपाइलर" (डीबी इंजन) इसके बजाय एक इंडेक्स लुकअप कर सकता है - जिसमें पूरी तरह से अलग प्रदर्शन विशेषताएं हैं। लेकिन चूँकि SQL इतना उच्च-स्तर का है, इसलिए "कंपाइलर" पूरी तरह से अलग एल्गोरिदम को स्थानापन्न कर सकता है, कई प्रोसेसर या I / O चैनल या पूरे सर्वर को पारदर्शी रूप से लागू कर सकता है, और बहुत कुछ।

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

इसे देखने का एक और तरीका हो सकता है ... हास्केल को तेज क्यों नहीं होना चाहिए ? यह क्या करता है कि इसे धीमा करना चाहिए?

यह पर्ल या जावास्क्रिप्ट जैसी व्याख्या वाली भाषा नहीं है। यह जावा या C # जैसी वर्चुअल मशीन प्रणाली भी नहीं है। यह देशी मशीन कोड के लिए नीचे सभी तरह से संकलित करता है, इसलिए कोई उपरि नहीं है।

OO भाषाओं के विपरीत [Java, C #, JavaScript…], Haskell में पूर्ण प्रकार का क्षरण है [जैसे C, C ++, पास्कल…]। सभी प्रकार की जाँच केवल संकलन-समय पर होती है। इसलिए आपको धीमा करने के लिए कोई रन-टाइम टाइप-चेकिंग नहीं है। (उस मामले के लिए कोई नल-पॉइंटर चेक नहीं है। जावा, जेवीएम को अशक्त बिंदुओं की जांच करनी चाहिए और यदि आप एक को टालते हैं तो अपवाद को फेंक दें। हास्केल को उस चेक से परेशान नहीं होना चाहिए।)

आप कहते हैं कि यह "रन-टाइम में फ़्लाई पर फ़ंक्शंस बनाने" के लिए धीमा लगता है, लेकिन यदि आप बहुत ध्यान से देखते हैं, तो आप वास्तव में ऐसा नहीं करते हैं। यह आप की तरह लग सकता है, लेकिन आप नहीं करते। यदि आप कहते हैं (+5) , ठीक है, तो यह आपके स्रोत कोड में हार्ड-कोडित है। यह रन-टाइम में नहीं बदल सकता है। तो यह वास्तव में एक गतिशील कार्य नहीं है। यहां तक ​​कि करीकृत कार्य वास्तव में सिर्फ डेटा ब्लॉक में मापदंडों को सहेज रहे हैं। सभी निष्पादन योग्य कोड वास्तव में संकलन-समय पर मौजूद हैं; कोई रन-टाइम व्याख्या नहीं है। (कुछ अन्य भाषाओं के विपरीत, जिनमें "eval function" है।)

पास्कल के बारे में सोचो। यह पुराना है और कोई भी वास्तव में इसका उपयोग नहीं करता है, लेकिन कोई भी शिकायत नहीं करेगा कि पास्कल धीमा है । इसके बारे में नापसंद करने के लिए बहुत सारी चीजें हैं, लेकिन धीमेपन वास्तव में उनमें से एक नहीं है। हास्केल वास्तव में ऐसा नहीं कर रहा है जो मैन्युअल मेमोरी प्रबंधन के बजाय कचरा संग्रह के अलावा पास्कल से अलग है। और अपरिवर्तनीय डेटा GC इंजन के लिए कई अनुकूलन की अनुमति देता है [जो आलसी मूल्यांकन फिर कुछ हद तक जटिल होता है]।

मुझे लगता है कि बात यह है कि हास्केल उन्नत और परिष्कृत और उच्च-स्तरीय लग रहा है, और हर कोई सोचता है "ओह वाह, यह वास्तव में शक्तिशाली है, यह आश्चर्यजनक रूप से धीमा होना चाहिए! " लेकिन यह नहीं है। या कम से कम, यह उस तरह से नहीं है जैसा आप उम्मीद करेंगे। हां, यह एक अद्भुत प्रकार की प्रणाली है। लेकिन आप जानते हैं कि क्या? यह सब संकलन-समय पर होता है। रन-टाइम तक, यह चला गया। हां, यह आपको कोड की एक पंक्ति के साथ जटिल ADTs बनाने की अनुमति देता है। लेकिन आप जानते हैं कि क्या? एक ADT सिर्फ struct का एक सामान्य साधारण सी union है। और कुछ नहीं।

असली हत्यारा आलसी मूल्यांकन है। जब आपको अपने कोड की कठोरता / आलस्य मिलता है, तो आप मूर्खतापूर्ण तेज़ कोड लिख सकते हैं जो अभी भी सुरुचिपूर्ण और सुंदर है। लेकिन अगर आपको यह सामान गलत लगता है, तो आपका कार्यक्रम हजारों बार धीमा हो जाता है, और यह वास्तव में गैर-स्पष्ट है कि ऐसा क्यों हो रहा है।

उदाहरण के लिए, मैंने यह गिनने के लिए एक तुच्छ सा प्रोग्राम लिखा कि एक फाइल में कितनी बार प्रत्येक बाइट दिखाई देती है। 25KB इनपुट फ़ाइल के लिए, प्रोग्राम को चलाने में 20 मिनट लगे और 6 गीगाबाइट रैम को निगल लिया! वह बेतुका है !! लेकिन तब मुझे एहसास हुआ कि समस्या क्या थी, एक एकल बैंग-पैटर्न जोड़ा, और रन-टाइम 0.02 सेकंड तक गिरा।

यह वह जगह है जहां हास्केल अप्रत्याशित रूप से धीरे-धीरे जाता है। और यह निश्चित है कि इसकी आदत पड़ने में थोड़ा समय लगता है। लेकिन समय के साथ, वास्तव में तेज कोड लिखना आसान हो जाता है।

हास्केल इतनी तेजी से क्या करता है? पवित्रता। स्थैतिक प्रकार। आलस्य। लेकिन इन सबसे ऊपर, पर्याप्त रूप से उच्च-स्तरीय होने के नाते संकलक आपके कोड की अपेक्षाओं को तोड़ने के बिना कार्यान्वयन को मौलिक रूप से बदल सकता है।

लेकिन मुझे लगता है कि यह सिर्फ मेरी राय है ...






lambda-calculus