प्रदर्शन: जावास्क्रिप्ट में पुनरावृत्ति बनाम पुनरावृत्ति


24

मैंने हाल ही में जावास्क्रिप्ट के कार्यात्मक पहलुओं और योजना और जावास्क्रिप्ट के बीच संबंधों के बारे में ( http://dailyjs.com/2012/09/14/functional-programming/ ) कुछ लेख पढ़े हैं (बाद वाला पहले से प्रभावित था, जो एक कार्यात्मक भाषा है, जबकि OO पहलुओं को स्व से विरासत में मिला है जो एक प्रोटोटाइप आधारित भाषा है)।

हालाँकि मेरा प्रश्न अधिक विशिष्ट है: मैं सोच रहा था कि क्या जावास्क्रिप्ट में पुनरावृत्ति बनाम पुनरावृत्ति के प्रदर्शन के बारे में मीट्रिक हैं।

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


3
अपना खुद का परीक्षण करें और jsperf.com
TehShrike

TCO का उल्लेख करने वाले इनाम और एक उत्तर के साथ। ऐसा प्रतीत होता है कि ES6 TCO को निर्दिष्ट करता है, लेकिन अभी तक कोई भी इसे लागू नहीं करता है यदि हम मानते हैं कि kangax.github.io/compat-table/es6 क्या मैं कुछ याद कर रहा हूं?
मथायस कौएर

जवाबों:


28

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

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


बस सोच रहा था ... मैंने एक बिट कोड देखा है जहां एक खाली सरणी बनाई जाती है और पुनरावर्ती फ़ंक्शन साइट को सरणी में स्थिति में सौंपा जाता है और फिर सरणी में संग्रहीत मान वापस आ जाता है। क्या इसका मतलब यह है कि "पुनरावृत्ति को अपने स्वयं के ढेर के साथ बदलें"? उदा: var stack = []; var factorial = function(n) { if(n === 0) { return 1 } else { stack[n-1] = n * factorial(n - 1); return stack[n-1]; } }
मस्तज़ी

@ मस्तज़ी: नहीं, यह आंतरिक के साथ एक बेकार कॉल स्टैक बना देगा। मेरा मतलब था विकिपीडिया से कतार आधारित बाढ़-भराव
त्रिकोण ३

यह ध्यान देने योग्य है कि एक भाषा TCO प्रदर्शन नहीं करती है, लेकिन एक कार्यान्वयन हो सकता है। जिस तरह से लोग JS को ऑप्टिमाइज़ करते हैं
उसका

1
@mastazi elseउस फ़ंक्शन को बदलें else if (stack[n-1]) { return stack[n-1]; } elseऔर आपके पास मेमोइज़ेशन है । जिसने भी यह लिखा है कि तथ्यात्मक कोड का अपूर्ण कार्यान्वयन था (और शायद stack[n]इसके बजाय हर जगह उपयोग किया जाना चाहिए stack[n-1])।
इजाकाता

धन्यवाद @ इज़कट, मैं अक्सर इस तरह का अनुकूलन करता हूं लेकिन आज तक मुझे इसका नाम नहीं पता था। मुझे आईटी के बजाय सीएस का अध्ययन करना चाहिए ;-)
mastazi

20

अद्यतन : ES2015 के बाद से, जावास्क्रिप्ट में TCO है , इसलिए नीचे दिए गए तर्क का हिस्सा अब खड़ा नहीं होता है।


यद्यपि जावास्क्रिप्ट में टेल कॉल ऑप्टिमाइज़ेशन नहीं है, फिर भी अक्सर जाना सबसे अच्छा तरीका है। और ईमानदारी से, किनारे के मामलों को छोड़कर, आपको कॉल स्टैक ओवरफ्लो नहीं मिलेगा।

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

पुनरावृत्ति हमेशा अधिक सुरुचिपूर्ण होती है 1

1 : व्यक्तिगत राय।


3
मैं मानता हूं कि पुनरावृत्ति अधिक सुरुचिपूर्ण है, और लालित्य महत्वपूर्ण है क्योंकि यह पठनीयता के साथ-साथ रखरखाव है (यह व्यक्तिपरक है, लेकिन मेरी राय में पुनरावृत्ति को पढ़ना बहुत आसान है, इस प्रकार बनाए रखने योग्य है)। हालांकि, कभी-कभी प्रदर्शन मायने रखता है; क्या आप इस दावे का समर्थन कर सकते हैं कि पुनरावृत्ति जाने का सबसे अच्छा तरीका है, यहां तक ​​कि प्रदर्शन-वार भी?
मस्ताज़ी

3
@mastazi ने मेरे जवाब में कहा, मुझे संदेह है कि पुनरावृत्ति आपकी अड़चन बनने वाली है। ज्यादातर बार, यह डोम हेरफेर है, या अधिक सामान्यतः I / O है। और यह मत भूलो कि समयपूर्व अनुकूलन सभी बुराइयों की जड़ है;)
फ्लोरियन मार्गाइन

डोम हेरफेर के लिए +1 अधिकांश समय एक अड़चन है! मुझे इस बारे में येहुदा काट्ज़ (एम्बर.जेएस) का एक बहुत दिलचस्प साक्षात्कार याद है।
मस्तजी 12

1
@ माइक "समय से पहले" की परिभाषा " उचित समय से पहले परिपक्व या पका हुआ है।" यदि आप जानते हैं कि पुनरावर्ती रूप से कुछ करने से स्टैकओवरफ्लो होगा, तो यह समय से पहले नहीं है। हालांकि, यदि आप किसी भी वास्तविक डेटा (बिना किसी वास्तविक डेटा) पर भरोसा करते हैं, तो यह समय से पहले है।
जीरक

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

7

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

कोड: https://github.com/j03m/trickyQuestions/blob/master/factorial.js

Result:
j03m-MacBook-Air:trickyQuestions j03m$ node factorial.js 
Time:557
Time:126
j03m-MacBook-Air:trickyQuestions j03m$ node factorial.js 
Time:519
Time:120
j03m-MacBook-Air:trickyQuestions j03m$ node factorial.js 
Time:541
Time:123
j03m-MacBook-Air:trickyQuestions j03m$ node --version
v0.8.22

आप इस के साथ कोशिश कर सकते हैं "use strict";और देखें कि क्या इससे कोई फर्क पड़ता है। (यह jumpमानक कॉल अनुक्रम के बजाय एस का उत्पादन करेगा )
बर्डॉक

1
नोड (6.9.1) के हालिया संस्करण में मुझे बेहद समान परिणाम मिले। पुनरावर्तन पर थोड़ा कर है, लेकिन मैं इसे एक किनारे का मामला मानता हूं - 1,000,000 लूप के लिए 400ms का अंतर ।0025 एमएस लूप है। यदि आप 1,000,000 छोरों कर रहे हैं तो यह ध्यान में रखना कुछ है।
केलज़

6

ओपी के अनुरोध के अनुसार मैं चिप लगाऊंगा (बिना खुद को बेवकूफ बनाए, उम्मीद है: पी)

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

जहाँ तक जेएस टेल-कॉल ऑप्टिमाइज़ेशन नहीं करने वाले तर्क का सवाल है: यह पूरी तरह से सच नहीं है, ECMA5 के सख्त मोड का उपयोग करने से एससीओ सक्षम होता है। यह कुछ समय पहले मैं बहुत खुश नहीं था, लेकिन कम से कम अब मुझे पता है कि arguments.calleeसख्त मोड में त्रुटियों को क्यों फेंकता है। मुझे एक बग रिपोर्ट के लिंक के ऊपर लिंक पता है, लेकिन बग WONTFIX पर सेट है। इसके अलावा, मानक TCO आ रहा है: ECMA6 (दिसंबर 2013)।

सहज, और जेएस की कार्यात्मक प्रकृति से चिपके हुए, मैं कहूंगा कि पुनरावृत्ति समय की 99.99% अधिक कुशल कोडिंग शैली है। हालांकि, फ्लोरियन मार्गाइन के पास एक बिंदु है जब वह कहते हैं कि अड़चन कहीं और पाए जाने की संभावना है। यदि आप DOM में हेरफेर कर रहे हैं, तो आप संभवतः अपने कोड को यथासंभव लिखने योग्य बना सकते हैं। DOM API वह है जो: धीमा है।

मुझे लगता है कि यह एक निश्चित जवाब देने के लिए असंभव है जिस पर तेज विकल्प है। हाल ही में, बहुत सारे jspref के शो मैंने देखे हैं कि क्रोम का V8 इंजन कुछ कार्यों में हास्यास्पद रूप से तेज है, जो एफएफ के स्पाइडरमोंकी पर 4x धीमा चलाते हैं और इसके विपरीत। आधुनिक जेएस इंजन में आपके कोड को अनुकूलित करने के लिए अपनी आस्तीन के सभी प्रकार हैं। मैं कोई विशेषज्ञ नहीं हूं, लेकिन मुझे लगता है कि V8, उदाहरण के लिए, क्लोजर (और पुनरावृत्ति) के लिए अत्यधिक अनुकूलित है, जबकि एमएस का JScript इंजन नहीं है। स्पाइडरमोंकी अक्सर डोम के शामिल होने पर बेहतर प्रदर्शन करती है ...

संक्षेप में: मैं कहूंगा कि कौन सी तकनीक अधिक प्रदर्शनकारी होगी, जैसा कि जेएस में हमेशा होता है, भविष्यवाणी करना असंभव है।


3

सख्त मोड के बिना, Iteration प्रदर्शन आमतौर पर थोड़ा तेज होता है, तो पुनरावृत्ति ( JIT को और अधिक काम करने के अलावा )। पूंछ पुनरावृत्ति अनुकूलन अनिवार्य रूप से किसी भी ध्यान देने योग्य अंतर को समाप्त कर देता है क्योंकि यह संपूर्ण कॉल अनुक्रम को छलांग में बदल देता है।

उदाहरण: जेस्पर

जब यह पुनरावृत्ति और पुनरावृत्ति के बीच चयन करने की बात आती है तो मैं कोड स्पष्टता और सरलता के बारे में अधिक चिंता करने का सुझाव दूंगा।

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