क्या अपरिवर्तनीयता जावास्क्रिप्ट में प्रदर्शन को प्रभावित करती है?


88

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

मेरी प्रारंभिक प्रतिक्रिया है, ऐसा लगता है कि यह प्रदर्शन के लिए बुरा होगा।

लेकिन तब की तरह पुस्तकालयों Immutable.js और Redux.js मुझे अधिक चालाक लोगों द्वारा लिखे गए हैं, और प्रदर्शन के लिए एक मजबूत चिंता का विषय है लगता है, तो यह मुझे आश्चर्य है कि अगर कचरा की मेरी समझ (और इसके प्रदर्शन प्रभाव) गलत है बनाता है।

क्या मेरे द्वारा लापता की गई अपरिहार्यता के लिए प्रदर्शन लाभ हैं, और क्या वे इतना कचरा बनाने के डाउनसाइड को पछाड़ते हैं?


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

8
मेरे अनुभव में, प्रदर्शन केवल दो परिदृश्यों के लिए एक वैध चिंता का विषय है - एक, जब एक क्रिया को एक सेकंड में 30+ बार किया जाता है, और दो - जब इसका प्रभाव प्रत्येक निष्पादन के साथ बढ़ता है (विंडोज एक्सपी ने एक बार एक बग पाया जहां विंडोज अपडेट का समय लगा अपने इतिहास में हर अपडेट केO(pow(n, 2)) लिए ।) अधिकांश अन्य कोड किसी घटना के लिए तत्काल प्रतिक्रिया है; एक क्लिक, एपीआई अनुरोध, या इसी तरह, और जब तक निष्पादन का समय स्थिर होता है, किसी भी संख्या की वस्तुओं की सफाई शायद ही मायने रखती है।
कटाना ३१४

4
इसके अलावा, विचार करें कि अपरिवर्तनीय डेटा संरचनाओं के कुशल कार्यान्वयन मौजूद हैं। हो सकता है कि ये आपस में उतने कुशल न हों, लेकिन शायद भोले-भाले क्रियान्वयन से कहीं अधिक कुशल हैं। उदाहरण के लिए देखें क्रिस ओकासाकी द्वारा शुद्ध रूप से कार्यात्मक डेटा संरचना
जियोर्जियो

1
@ Katana314: मेरे लिए 30+ बार अभी भी प्रदर्शन के बारे में चिंता करने के लिए पर्याप्त नहीं होगा। मैंने एक छोटा सीपीयू एमुलेटर जो मैंने नोड.जेएस को लिखा था और नोड ने वर्चुअल सीपीयू को लगभग 20MHz (प्रति 20 मिलियन बार प्रति सेकंड) पर निष्पादित किया। इसलिए मुझे केवल प्रदर्शन के बारे में चिंता है अगर मैं प्रति सेकंड 1000+ बार कुछ कर रहा था (तब भी, मैं वास्तव में तब तक चिंता नहीं करूंगा जब तक कि मैं प्रति सेकंड 1000000 ऑपरेशन न करूं क्योंकि मुझे पता है कि मैं आराम से 10 से अधिक बार एक साथ कर सकता हूं) ।
स्लीपबेटमैन

2
@RobertHarvey "अपरिवर्तनीयता, सभी अपने आप में, केवल इस अर्थ में प्रदर्शन लाभ है कि यह बहु-थ्रेडेड कोड लिखना आसान बनाता है।" यह पूरी तरह से सच नहीं है, अपरिवर्तनीयता बिना किसी वास्तविक परिणाम के बहुत व्यापक साझाकरण की अनुमति देती है। जो एक परस्पर वातावरण में बहुत असुरक्षित है। यह आपको O(1)सरणी स्लाइसिंग और O(log n)बाइनरी ट्री में डालने की तरह सोचता है जबकि अभी भी पुराने को स्वतंत्र रूप से उपयोग करने में सक्षम है, और एक अन्य उदाहरण है tailsजो सूची के सभी पूंछों को tails [1, 2] = [[1, 2], [2], []]केवल O(n)समय और स्थान लेता है, लेकिन O(n^2)तत्व गणना में है
अर्धविराम

जवाबों:


59

उदाहरण के लिए, यदि आपको किसी वस्तु की एकल संपत्ति को बदलने की आवश्यकता है, तो नई संपत्ति के साथ एक पूरी नई वस्तु बनाने के लिए बेहतर है, और पुरानी वस्तु से अन्य सभी गुणों की नकल करें, और पुरानी वस्तु को कचरा एकत्र होने दें।

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

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

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

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

इस स्थिति के लिए अंगूठे का मेरा व्यक्तिगत नियम है:

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

इन दो चरम सीमाओं के बीच स्थितियों के लिए, अपने फैसले का उपयोग करें। लेकिन YMMV।


8
That will leave a lot more garbage than in your case.और मामले को बदतर बनाने के लिए, आपका रनटाइम संभवतः व्यर्थ दोहराव का पता लगाने में असमर्थ होगा, और इस प्रकार (एक समाप्त अपरिवर्तनीय वस्तु के अलावा कोई भी उपयोग नहीं कर रहा है) यह संग्रह के लिए भी योग्य नहीं होगा।
याकूब रायहल

37

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

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

मेरे पास कार्यात्मक प्रोग्रामिंग कक्षाओं में लोगों की टिप्पणी है कि मैं सिखाता हूं कि एक एल्गोरिथ्म कितना कचरा बनाता है, फिर मैं उसी एल्गोरिथ्म के विशिष्ट अनिवार्य संस्करण को दिखाता हूं जो केवल उतना ही बनाता है। बस किसी कारण से लोग अब इसे नोटिस नहीं करते हैं।

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


8
"... फिर मैं उसी एल्गोरिथ्म का विशिष्ट अनिवार्य संस्करण दिखाता हूं जो केवल उतना ही बनाता है।" इस। इसके अलावा, जो लोग इस शैली के लिए नए हैं, और खासकर अगर वे सामान्य रूप से कार्यात्मक शैली के लिए नए हैं, तो शुरू में उप-इष्टतम कार्यात्मक कार्यान्वयन का उत्पादन कर सकते हैं।
wberry

1
"चर बनाने को हतोत्साहित करना" क्या केवल उन भाषाओं के लिए मान्य नहीं है जहाँ डिफ़ॉल्ट व्यवहार असाइनमेंट / निहित निर्माण पर कॉपी है? जावास्क्रिप्ट में, एक चर सिर्फ एक पहचानकर्ता है; यह अपने आप में कोई वस्तु नहीं है। यह अभी भी कहीं न कहीं जगह घेरता है, लेकिन यह नगण्य है (विशेष रूप से जावास्क्रिप्ट के अधिकांश कार्यान्वयन के रूप में, मेरी जानकारी के लिए, अभी भी फ़ंक्शन कॉल के लिए एक स्टैक का उपयोग करते हैं, जिसका अर्थ है कि जब तक आपके पास बहुत सारी पुनरावृत्ति नहीं हो जाती है तब तक आप अधिकांश के लिए एक ही स्टैक स्थान का पुन: उपयोग कर सकते हैं। अस्थायी चर)। अपरिवर्तनशीलता का उस पहलू से कोई संबंध नहीं है।
JAB

33

इस प्रश्नोत्तर के लिए देर से आने वाला, पहले से ही शानदार उत्तरों के साथ, लेकिन मैं एक विदेशी के रूप में घुसपैठ करना चाहता था क्योंकि स्मृति में बिट्स और बाइट्स के निचले स्तर के दृष्टिकोण से चीजों को देखता था।

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

धीमी तेज़ /

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

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

अपरिवर्तनीय बिट्स और बाइट्स को बदलना

निम्न-स्तर के दृष्टिकोण से आ रहा है, अगर हम एक्स-रे अवधारणाओं को पसंद करते हैं objectsऔर stringsइसके आगे, इसके दिल में बस बिट्स और बाइट्स हैं जो विभिन्न रूपों में विभिन्न गति / आकार विशेषताओं (मेमोरी हार्डवेयर की गति और आकार) के साथ आम तौर पर होते हैं परस्पर अनन्य)।

यहाँ छवि विवरण दर्ज करें

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

हम अंत में उस प्रक्रिया में एक रिंच फेंकते हैं, यदि यह मेमोरी को संशोधित करता है तो पक्ष में एक नया मेमोरी ब्लॉक बनाना चाहता है, जैसे:

यहाँ छवि विवरण दर्ज करें

... इस मामले में, नए मेमोरी ब्लॉक तक पहुंचने के लिए अनिवार्य पेज दोष और कैश मिस की आवश्यकता हो सकती है ताकि इसे मेमोरी के सबसे तेज़ रूपों (सभी तरह रजिस्टर में) में वापस ले जाया जा सके। यह एक वास्तविक प्रदर्शन हत्यारा हो सकता है।

इसे कम करने के तरीके हैं, हालांकि, पहले से स्पर्श किए गए प्रचारित मेमोरी के आरक्षित पूल का उपयोग करते हुए।

बिग एग्रीगेट्स

एक और वैचारिक मुद्दा जो थोड़े उच्च स्तर के दृष्टिकोण से उत्पन्न होता है, वह वास्तव में थोक में वास्तव में बड़े समुच्चय की अनावश्यक प्रतियों का काम करता है।

एक अत्यधिक जटिल आरेख से बचने के लिए, आइए कल्पना करें कि यह सरल मेमोरी ब्लॉक किसी तरह महंगा था (शायद अविश्वसनीय सीमित हार्डवेयर पर UTF-32 वर्ण)।

यहाँ छवि विवरण दर्ज करें

इस मामले में, यदि हम "HELP" को "KILL" से बदलना चाहते थे और यह मेमोरी ब्लॉक अपरिवर्तनीय था, तो हमें एक अद्वितीय नई ऑब्जेक्ट बनाने के लिए इसकी संपूर्णता में एक नया ब्लॉक बनाना होगा, भले ही इसके कुछ भाग बदल गए हों :

यहाँ छवि विवरण दर्ज करें

हमारी कल्पना को थोड़ा सा बढ़ाते हुए, इस तरह की गहरी नकल बाकी सब कुछ सिर्फ एक छोटा सा हिस्सा बनाने के लिए काफी महंगी हो सकती है (वास्तविक दुनिया के मामलों में, यह मेमोरी ब्लॉक बहुत अधिक होगा, किसी समस्या को हल करने के लिए बहुत बड़ा)।

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

इस निरर्थक नकल के काम को कम करने का एक तरीका यह है कि इन मेमोरी ब्लॉक्स को वर्णों (या संदर्भों) के संग्रह में पात्रों के रूप में बनाया जाए:

क्षमायाचना, मैं यह महसूस करने में विफल रहा Lकि आरेख बनाते समय हमें अद्वितीय बनाने की आवश्यकता नहीं है ।

ब्लू उथले कॉपी किए गए डेटा को इंगित करता है।

यहाँ छवि विवरण दर्ज करें

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

यहां तक ​​कि अगर हम सावधानी से इन पात्रों को आवंटित करने के लिए सावधान थे, तो कहो कि मशीन 8 वर्णों और 8 बिंदुओं को एक चरित्र को कैश लाइन में लोड कर सकती है। हम नई स्ट्रिंग को पार करने के लिए इस तरह से लोडिंग मेमोरी को समाप्त करते हैं:

यहाँ छवि विवरण दर्ज करें

इस मामले में, हम इस स्ट्रिंग को पार करने के लिए सन्निहित स्मृति के लायक 7 अलग कैश लाइनों की आवश्यकता को समाप्त करते हैं, जब आदर्श रूप से हमें केवल 3 की आवश्यकता होती है।

डेटा का हिस्सा

उपरोक्त समस्या को कम करने के लिए, हम एक ही मूल रणनीति को लागू कर सकते हैं, लेकिन 8 वर्णों के मोटे स्तर पर, जैसे

यहाँ छवि विवरण दर्ज करें

परिणाम को 4 स्ट्रिंग लाइनों की आवश्यकता होती है, जिसमें डेटा (3 पॉइंटर्स के लिए 1, और पात्रों के लिए 3) इस स्ट्रिंग को पार करने के लिए लोड किया जाना चाहिए जो सैद्धांतिक इष्टतम का केवल 1 छोटा है।

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

गति

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

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

नया और पुराना डाटा रखना

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

उदाहरण: पूर्ववत करें

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

उच्च स्तरीय इंटरफेस

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

संभवतः उन मामलों में सबसे आसान तरीका एक फ़ंक्शन (जहां वे आमतौर पर हमें यात्रा नहीं करते हैं) के संदर्भ में स्थानीय रूप से म्यूटेबल बफ़र्स का उपयोग करना है, जो एक नई अपरिवर्तनीय प्रतिलिपि प्राप्त करने के लिए डेटा संरचना में एटोमिक रूप से परिवर्तन करते हैं (मुझे विश्वास है कि कुछ भाषाएं कॉल करती हैं ये "ग्राहक") ...

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

डेटा संरचनाएं

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

लिंक की गई सूचियाँ इसे समायोजित करने और अनियंत्रित सूचियों में बदलने के लिए बहुत थोड़ा बदलना चाह सकती हैं। रैंडम एक्सेस के लिए मॉडुलो इंडेक्सिंग के साथ बड़े, सन्निहित सरणियों को सन्निहित खंडों में बदल दिया जा सकता है।

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

प्रदर्शन

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

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

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


11

ImmutableJS वास्तव में काफी कुशल है। यदि हम एक उदाहरण लेते हैं:

var x = {
    Foo: 1,
    Bar: { Baz: 2 }
    Qux: { AnotherVal: 3 }
}

यदि उपरोक्त वस्तु अपरिवर्तनीय है, तो आप 'बाज' संपत्ति के मूल्य को संशोधित करते हैं जो आपको मिलेगा:

var y = x.setIn('/Bar/Baz', 3);
y !== x; // Different object instance
y.Bar !== x.Bar // As the Baz property was changed, the Bar object is a diff instance
y.Qux === y.Qux // Qux is the same object instance

यह गहरी ऑब्जेक्ट मॉडल के लिए कुछ वास्तव में अच्छा प्रदर्शन सुधार बनाता है, जहां आपको रूट पर रूट पर ऑब्जेक्ट्स पर मूल्य प्रकारों की प्रतिलिपि बनाने की आवश्यकता होती है। जितना बड़ा ऑब्जेक्ट मॉडल और आपके द्वारा किए जाने वाले बदलाव उतने ही बेहतर, मेमोरी और CPU प्रदर्शन अपरिवर्तनीय डेटा संरचना के रूप में वे बहुत सारी वस्तुओं को साझा करते हैं।

जैसा कि अन्य उत्तरों में कहा गया है, यदि आप xइसे एक फ़ंक्शन में इसे पारित करने से पहले रक्षात्मक कॉपी करके समान गारंटी प्रदान करने की कोशिश करने के लिए इसके विपरीत हैं, तो यह हेरफेर कर सकता है तो प्रदर्शन काफी बेहतर है।


4

एक सीधी रेखा में, अपरिवर्तनीय कोड में ऑब्जेक्ट निर्माण का ओवरहेड होता है, जो धीमा होता है। हालाँकि, ऐसी बहुत सी स्थितियाँ हैं जहाँ उत्परिवर्तनीय कोड को कुशलतापूर्वक प्रबंधित करना बहुत मुश्किल हो जाता है (परिणामस्वरूप बहुत सी रक्षात्मक प्रतिलिपि, जो महंगी भी है), और किसी वस्तु को 'कॉपी' करने की लागत को कम करने के लिए बहुत सारी चतुर रणनीतियाँ हैं। , जैसा कि दूसरों द्वारा उल्लेख किया गया है।

यदि आपके पास एक काउंटर जैसी कोई वस्तु है, और यह एक सेकंड में कई बार बढ़ रही है, तो उस काउंटर को अपरिवर्तनीय होना प्रदर्शन जुर्माना के लायक नहीं हो सकता है। यदि आपके पास कोई ऐसी वस्तु है जो आपके एप्लिकेशन के कई अलग-अलग हिस्सों द्वारा पढ़ी जा रही है, और उनमें से प्रत्येक वस्तु का अपना थोड़ा अलग क्लोन चाहता है, तो आपके पास एक अच्छा उपयोग करके ऑर्केस्ट्रेट करने में बहुत आसान समय होगा। अपरिवर्तनीय वस्तु कार्यान्वयन।


4

इस पर जोड़ने के लिए (पहले से ही उत्कृष्ट उत्तर दिया गया) प्रश्न:

संक्षिप्त उत्तर हाँ है ; यह प्रदर्शन को नुकसान पहुंचाएगा क्योंकि आप केवल मौजूदा वस्तुओं को म्यूट करने के बजाय ऑब्जेक्ट बना रहे हैं, जिसके परिणामस्वरूप अधिक ऑब्जेक्ट निर्माण ओवरहेड होता है।


हालांकि, लंबा जवाब वास्तव में नहीं है

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

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

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

अपवाद हैं, सबसे विशेष रूप से ऊपर बताए गए डेटा संरचनाएं। मैं शायद ही कभी ऐसे परिदृश्य में आया हूँ जहाँ मैं निर्माण के बाद एक मानचित्र को बदलना चाहता था (हालाँकि माना जाता है कि मैं ES6 मानचित्रों के बजाय छद्म-वस्तु-शाब्दिक मानचित्रों के बारे में बात कर रहा हूँ), उसी के लिए सरणियाँ। जब आप बड़े डेटा स्ट्रक्चर्स के साथ काम कर रहे होते हैं, तो परिवर्तनशीलता भुगतान कर सकती है। याद रखें कि जावास्क्रिप्ट में प्रत्येक वस्तु को एक मूल्य के बजाय एक संदर्भ के रूप में पारित किया जाता है।


उस ने कहा, ऊपर लाया गया एक बिंदु जीसी और नकल का पता लगाने में असमर्थता थी। यह एक वैध चिंता है, लेकिन मेरे विचार में यह केवल एक चिंता है जब स्मृति एक चिंता का विषय है, और अपने आप को एक कोने में कोड करने के लिए बहुत आसान तरीके हैं - उदाहरण के लिए, क्लोजर में परिपत्र संदर्भ।


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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.