क्या कार्यात्मक प्रोग्रामिंग मल्टीथ्रेडिंग में तेज है क्योंकि मैं चीजों को अलग तरह से लिखता हूं या क्योंकि चीजें अलग तरह से संकलित हैं?


63

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

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

अगर मैं स्काला (जेवीएम का उपयोग करता हूं) में एक ही कार्यक्रम लिखता हूं, तो क्या यह कार्यान्वयन तेज होगा? यदि हाँ, तो क्यों? क्या यह लेखन शैली के कारण है? यदि यह है क्योंकि लेखन शैली का, अब है कि जावा लैम्ब्डा भाव भी शामिल है, मैं एक ही परिणाम लैम्ब्डा के साथ जावा का उपयोग कर प्राप्त नहीं कर सकता है? या यह तेज़ है क्योंकि स्काला चीजों को अलग तरह से संकलित करेगा?


64
Afaik कार्यात्मक प्रोग्रामिंग तेजी से मल्टीथ्रेडिंग नहीं बनाती है। यह लागू करने और सुरक्षित करने के लिए मल्टीथ्रेडिंग को आसान बनाता है क्योंकि कार्यात्मक प्रोग्रामिंग की कुछ विशेषताएं हैं जैसे कि अपरिवर्तनीयता और कार्यों के साइड इफेक्ट्स नहीं होते हैं जो इस संबंध में मदद करते हैं।
पीटर बी

7
ध्यान दें कि 1) बेहतर वास्तव में 2 परिभाषित नहीं है) यह निश्चित रूप से केवल "तेज" के रूप में परिभाषित नहीं किया गया है। एक भाषा X जिसे 0.1% प्रदर्शन के लिए Y के संबंध में एक बिलियन बार कोड के आकार की आवश्यकता होती है, किसी भी बेहतर परिभाषा के लिए Y से बेहतर नहीं है।
बाकुरि

2
क्या आपका मतलब "कार्यात्मक प्रोग्रामिंग" या "कार्यात्मक शैली में लिखे गए कार्यक्रम" के बारे में पूछना है? अक्सर तेज़ प्रोग्रामिंग से तेज़ प्रोग्राम नहीं होता है।
बेन वोइगट

1
यह मत भूलो कि हमेशा एक GC होता है जिसे पृष्ठभूमि में चलना होता है और अपनी आवंटन मांगों के साथ रहना पड़ता है ... और मुझे यकीन नहीं है कि यह मल्टीथ्रेडेड है ...
मेहरदाद

4
यहां सबसे सरल उत्तर यह है: कार्यात्मक प्रोग्रामिंग उन कार्यक्रमों को लिखने की अनुमति देता है जो कम दौड़ की स्थिति के मुद्दों पर विचार करेंगे , हालांकि इसका मतलब यह नहीं है कि अनिवार्य शैली लिखे जाने वाले कार्यक्रम धीमे होंगे।
दाविद पुरा

जवाबों:


97

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

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

हालाँकि, यह सब आकस्मिक है। यदि जावा में आपका समाधान भी परिवर्तनशील स्थिति (विशेष रूप से थ्रेड्स के बीच साझा किया गया) से बचा जाता है, तो इसे स्काला या क्लोजर जैसी कार्यात्मक भाषा में परिवर्तित करने से समवर्ती दक्षता के संदर्भ में कोई लाभ नहीं मिलेगा, क्योंकि मूल समाधान पहले से ही ओवरराइड की वजह से मुक्त है लॉकिंग और सिंकिंग तंत्र।

TL; DR: यदि जावा में एक की तुलना में स्केल में एक समाधान समानांतर प्रसंस्करण में अधिक कुशल है, तो यह उस तरह से नहीं है जैसे कोड को जेवीएम के माध्यम से संकलित या चलाया जाता है, बल्कि इसलिए क्योंकि जावा समाधान थ्रेड्स के बीच उत्परिवर्तनीय स्थिति साझा कर रहा है, या तो दौड़ की स्थिति पैदा कर रहा है या उन्हें बचने के लिए सिंक्रनाइज़ेशन के ओवरहेड को जोड़ रहा है।


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

31
एक थ्रेड होने से डेटा म्यूट हो जाता है जबकि अन्य इसे पढ़ते हैं यह पर्याप्त है कि आपको "विशेष देखभाल" शुरू करना है।
पीटर ग्रीन

10
@ ब्रेंडन: नहीं, यदि एक थ्रेड डेटा को संशोधित करता है जबकि अन्य थ्रेड्स उसी डेटा से पढ़ रहे हैं, तो आपके पास एक दौड़ की स्थिति है। यहां तक ​​कि केवल एक धागा को संशोधित करने पर भी विशेष देखभाल की आवश्यकता होती है।
कॉर्नस्टालक्स

3
समानांतर प्रसंस्करण के संदर्भ में उत्परिवर्तनीय स्थिति "सभी बुराई की जड़" है => यदि आपने अभी तक रस्ट को नहीं देखा है, तो मैं आपको सलाह देता हूं कि आप इसे देखें। यह इस बात का अहसास कराते हुए कि वह वास्तव में एलियासिंग है, अगर आप केवल एलियासिंग करते हैं या केवल परिवर्तनशीलता है, तो कोई समस्या नहीं है।
Matthieu M.

2
@MatthieuM। सही है, धन्यवाद! मैंने अपने उत्तर में चीजों को अधिक स्पष्ट रूप से व्यक्त करने के लिए संपादन किया। म्यूटेबल अवस्था केवल "सभी बुराई की जड़" है जब इसे समवर्ती प्रक्रियाओं के बीच साझा किया जाता है - इसके स्वामित्व नियंत्रण तंत्र के साथ जंग से बचा जाता है।
मिशेल हेनरिक

8

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

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


7

सामान्य प्रोग्राम के रूप में फ़ंक्शनल प्रोग्रामिंग तेज़ कार्यक्रमों के लिए नहीं है। यह जो बनाता है वह आसान समानांतर और समवर्ती प्रोग्रामिंग के लिए है। इसकी दो मुख्य कुंजी हैं:

  1. उत्परिवर्तनीय स्थिति से बचने के लिए उन चीजों की संख्या कम हो जाती है जो एक कार्यक्रम में गलत हो सकती हैं, और इससे भी अधिक समवर्ती कार्यक्रम में।
  2. उच्च-स्तरीय अवधारणाओं के पक्ष में साझा-मेमोरी और लॉक-आधारित सिंक्रोनाइज़ेशन प्राइमेटिविटी से बचने के लिए कोड के थ्रेड्स के बीच सिंक्रोनाइज़ेशन को सरल बनाया जाता है।

बिंदु # 2 का एक उत्कृष्ट उदाहरण यह है कि हास्केल में हम निर्धारक समानतावाद बनाम गैर-निर्धारक समरूपता के बीच एक स्पष्ट अंतर रखते हैं । हस्केल में साइमन मारलो की उत्कृष्ट पुस्तक समानांतर और समवर्ती प्रोग्रामिंग को उद्धृत करने से बेहतर कोई स्पष्टीकरण नहीं है (उद्धरण 1 अध्याय से हैं :

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

इसके विपरीत, संगामिति एक प्रोग्राम-स्ट्रक्चरिंग तकनीक है जिसमें नियंत्रण के कई सूत्र हैं। वैचारिक रूप से, नियंत्रण के धागे "एक ही समय में" निष्पादित होते हैं; अर्थात्, उपयोगकर्ता उनके प्रभाव को देखता है। चाहे वे वास्तव में एक ही समय में निष्पादित हों या नहीं एक कार्यान्वयन विवरण है; एक समवर्ती कार्यक्रम एक एकल प्रोसेसर पर interleaved निष्पादन या कई भौतिक प्रोसेसर के माध्यम से निष्पादित कर सकता है।

इसके अलावा, मार्लो में नियतत्ववाद के आयाम का भी उल्लेख किया गया है :

एक संबंधित अंतर निर्धारक और नोंडेटर्मिनिस्टिक प्रोग्रामिंग मॉडल के बीच है । एक नियतात्मक प्रोग्रामिंग मॉडल वह है जिसमें प्रत्येक प्रोग्राम केवल एक परिणाम दे सकता है, जबकि एक nondeterministic प्रोग्रामिंग मॉडल उन कार्यक्रमों को स्वीकार करता है जिनके निष्पादन के कुछ पहलू के आधार पर अलग-अलग परिणाम हो सकते हैं। समवर्ती प्रोग्रामिंग मॉडल अनिवार्य रूप से नोंडेटरमिनिस्टिक हैं क्योंकि उन्हें बाहरी एजेंटों के साथ बातचीत करनी चाहिए जो अप्रत्याशित समय पर घटनाओं का कारण बनते हैं। Nondeterminism में कुछ उल्लेखनीय कमियां हैं, हालांकि: कार्यक्रम परीक्षण और तर्क के लिए काफी कठिन हो जाते हैं।

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

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

  • समानता के लिए निर्धारक विशेषताएं और पुस्तकालय ।
  • गैर-निर्धारक सुविधाओं और पुस्तकालयों के लिए संगामिति

यदि आप सिर्फ एक शुद्ध, नियतात्मक संगणना को गति देने की कोशिश कर रहे हैं, तो नियतात्मक समानता होने से अक्सर चीजें बहुत आसान हो जाती हैं। अक्सर आप ऐसा कुछ करते हैं:

  1. एक फ़ंक्शन लिखें जो उत्तरों की एक सूची तैयार करता है, जिनमें से प्रत्येक गणना करने के लिए महंगा है, लेकिन एक दूसरे पर बहुत अधिक निर्भर नहीं है। यह हास्केल है, इसलिए सूचियां आलसी हैं- उनके तत्वों के मूल्य वास्तव में गणना नहीं किए जाते हैं जब तक कि उपभोक्ता उनसे मांग नहीं करता है।
  2. कई कोर में समानांतर में अपने फ़ंक्शन के परिणाम सूचियों का उपभोग करने के लिए रणनीतियाँ लाइब्रेरी का उपयोग करें।

मैंने वास्तव में कुछ सप्ताह पहले अपने एक खिलौना परियोजना कार्यक्रम के साथ ऐसा किया था । यह कार्यक्रम को समानांतर करने के लिए तुच्छ था - मुझे जो कुछ करना था, वह महत्वपूर्ण था, कुछ कोड जोड़ें जो कहता है कि "समानांतर में इस सूची के तत्वों की गणना करें" (पंक्ति 90), और मुझे एक निकट-रेखीय थ्रूपुट बढ़ावा मिला मेरे कुछ और महंगे टेस्ट केस।

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


2

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

यहां तक ​​कि जब आपको संशोधन चर की आवश्यकता होती है, तब भी आप आमतौर पर अलग-अलग उपयोग करके प्राप्त कर सकते हैं, और अपरिवर्तनीय चर के साथ। (हैस्केल में एक और अच्छी बात एसटीएम है, जो परमाणु संचालन के साथ ताले को बदल देता है, लेकिन मुझे यकीन नहीं है कि यह केवल कार्यात्मक प्रोग्रामिंग के लिए है या नहीं।) आमतौर पर, कार्यक्रम के केवल एक हिस्से को चीजों को बेहतर बनाने के लिए समानांतर बनाने की आवश्यकता होगी। प्रदर्शन के लिहाज से।

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

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


हास्केल (हास्केलविक) में आगे पठन समानता
और साइमन मार्लो द्वारा हास्केल में "वास्तविक दुनिया हास्केल" समानांतर और समवर्ती प्रोग्रामिंग में समवर्ती और मल्टीकोर प्रोग्रामिंग।


7
grep java this_postgrep scala this_postऔर grep jvm this_postकोई परिणाम नहीं लौटा :)
एंड्रेस एफ।

4
प्रश्न अस्पष्ट है। शीर्षक और पहले पैराग्राफ में, यह सामान्य रूप से कार्यात्मक प्रोग्रामिंग के बारे में पूछता है, दूसरे और तीसरे पैराग्राफ में, यह विशेष रूप से जावा और स्काला के बारे में पूछता है । यही कारण है कि दुर्भाग्य की बात है, खासकर जब से स्काला के मुख्य ताकतों में से एक ठीक तथ्य यह है कि यह है नहीं है (बस) एक कार्यात्मक भाषा। मार्टिन ओडस्की इसे "पोस्ट-फंक्शनल" कहते हैं, दूसरे इसे "ऑब्जेक्ट-फंक्शनल" कहते हैं। "कार्यात्मक प्रोग्रामिंग" शब्द की दो अलग-अलग परिभाषाएं हैं। एक है "प्रथम श्रेणी की प्रक्रियाओं के साथ प्रोग्रामिंग" (मूल परिभाषा जिसे LISP पर लागू किया गया है), दूसरा है ...
Jörg W Mittag

2
"संदर्भित रूप से पारदर्शी, शुद्ध, साइड-इफ़ेक्ट फ्री फ़ंक्शंस और अपरिवर्तनीय लगातार डेटा के साथ प्रोग्रामिंग" (एक बहुत सख्त, और नई व्याख्या भी)। यह उत्तर दूसरी व्याख्या को संबोधित करता है, जो समझ में आता है, क्योंकि क) पहली व्याख्या पूरी तरह से समानता और संगति से असंबंधित है, ख) पहली व्याख्या मूल रूप से अर्थहीन हो गई है क्योंकि सी के अपवाद के साथ लगभग हर भाषा में भी समान रूप से व्यापक प्रसार का उपयोग होता है आज की प्रथम श्रेणी की प्रक्रियाएँ हैं (जावा सहित), और सी) ओपी जावा और स्काला के बीच के अंतर के बारे में पूछती है, लेकिन कोई बात नहीं है ...
Jörg W Mittag

2
दोनों के बीच परिभाषा # 1 के बारे में, केवल परिभाषा # 2।
जोर्ज डब्ल्यू मित्तग

मूल्यांकन की बात बिलकुल सच नहीं है क्योंकि यह यहाँ लिखा गया है; डिफ़ॉल्ट रूप से, रनटाइम सभी में मल्टीथ्रेडिंग का उपयोग नहीं करता है, और IIRC यहां तक ​​कि अगर आप मल्टीथ्रेडिंग को सक्षम करते हैं तब भी आपको रनटाइम को यह बताना होगा कि उसे किन चीजों का समानांतर में मूल्यांकन करना चाहिए।
घन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.