क्या लूप्स वास्तव में रिवर्स में तेज हैं?


268

मैंने यह काफी बार सुना है। क्या पिछड़े गिनती करते समय जावास्क्रिप्ट लूप वास्तव में तेज होते हैं? यदि हां, तो क्यों? मैंने कुछ परीक्षण सूट उदाहरणों को दिखाते हुए देखा है कि उल्टे छोरों को तेज कर रहे हैं, लेकिन मुझे कोई स्पष्टीकरण नहीं मिल रहा है कि क्यों!

मैं यह मान रहा हूं क्योंकि लूप को अब किसी संपत्ति का मूल्यांकन करने के लिए हर बार यह देखने के लिए जांचना पड़ता है कि क्या यह समाप्त हो गया है और यह अंतिम सांख्यिक मूल्य के खिलाफ जांच करता है।

अर्थात

for (var i = count - 1; i >= 0; i--)
{
  // count is only evaluated once and then the comparison is always on 0.
}

6
Hehe। अनिश्चित काल के लिए ले जाएगा। कोशिश i--
StampedeXV

5
बैकवर्ड forलूप तेज़ होता है क्योंकि ऊपरी-बाउंड (hehe, लोअर-बाउंड) लूप कंट्रोल वैरिएबल को किसी ऑब्जेक्ट से परिभाषित या प्राप्त नहीं करना पड़ता है; यह एक निरंतर शून्य है।
जोश स्टोडोला

88
नहीं है कोई वास्तविक अंतर । मूल पाश निर्माण हमेशा बहुत तेज होने वाले हैं । उनके प्रदर्शन के बारे में चिंता मत करो।
जेम्स एलार्डिस

24
@Afshin: इस तरह के सवालों के लिए, कृपया हमें उन लेखों को दिखाएं जिन्हें आप संदर्भित कर रहे हैं।
ऑर्बिट

22
बहुत कम अंत और बैटरी चालित उपकरणों के लिए मुख्य रूप से महत्वपूर्ण अंतर है। अंतर यह है कि i-- के साथ आप लूप के अंत के लिए 0 की तुलना करते हैं, जबकि i ++ के साथ आप संख्या के साथ तुलना करते हैं। 0. मेरा मानना ​​है कि प्रदर्शन का अंतर 20 नैनोसेकंड (cmp कुल्हाड़ी जैसा कुछ, 0 बनाम cmp ax) जैसा था। , बीएक्स) - जो कुछ भी नहीं है, लेकिन यदि आप प्रति सेकंड हजारों बार लूप करते हैं, तो प्रत्येक के लिए 20 नैनोसेकेंड का लाभ क्यों नहीं होता है :)
लूबेन

जवाबों:


902

ऐसा नहीं है कि इससे i--तेज है i++। दरअसल, वे दोनों समान रूप से तेज हैं।

आरोही छोरों में क्या समय लगता है, प्रत्येक के लिए i, आपके सरणी का आकार मूल्यांकन कर रहा है । इस लूप में:

for(var i = array.length; i--;)

आप .lengthकेवल एक बार मूल्यांकन करते हैं, जब आप घोषणा करते हैं i, जबकि इस लूप के लिए

for(var i = 1; i <= array.length; i++)

जब आप जाँचें कि आप .lengthहर बार वेतन वृद्धि का मूल्यांकन करते हैं ।ii <= array.length

ज्यादातर मामलों में आपको इस तरह के अनुकूलन की चिंता नहीं करनी चाहिए


23
क्या यह array.length के लिए एक वैरिएबल को पेश करने और लूप के सिर के लिए उपयोग करने के लिए लायक है?
रागात्स्कीनेट

39
@ragatskynet: नहीं, जब तक आप एक बेंचमार्क सेट नहीं कर रहे हैं और एक बिंदु बनाना चाहते हैं।
जॉन

29
@ragatskynet यह निर्भर करता है: क्या .lengthएक निश्चित संख्या का मूल्यांकन करना या एक नए चर को घोषित करना और परिभाषित करना तेजी से होगा ? ज्यादातर मामलों में, यह समय से पहले (और गलत) अनुकूलन है, जब तक कि आपका lengthमूल्यांकन करना बहुत महंगा न हो।
alestanis

10
@ डॉ.रेडेल: यह तुलना नहीं है - यह मूल्यांकन है। 0से मूल्यांकन करने के लिए तेजी से है array.length। अच्छी तरह से, माना जाता है।
ऑर्बिट

22
गौर करने लायक बात यह है कि हम रूबी, अजगर जैसी व्याख्या की गई भाषाओं के बारे में बात कर रहे हैं। संकलित भाषाओं जैसे कि जावा में कंपाइलर स्तर पर अनुकूलन होते हैं जो इस अंतर को "सुचारू" करेंगे कि यह .lengthघोषणा में है for loopया नहीं, इससे कोई फर्क नहीं पड़ता ।
हारिस क्रजिना

198

इस लड़के ने बहुत सारे ब्राउज़रों में, जावास्क्रिप्ट में बहुत सारे छोरों की तुलना की। उसके पास एक टेस्ट सूट भी है ताकि आप उन्हें खुद चला सकें।

सभी मामलों में (जब तक कि मैंने अपने पढ़ने में एक को याद नहीं किया) सबसे तेज लूप था:

var i = arr.length; //or 10
while(i--)
{
  //...
}

नाइस :) लेकिन "-लोप टेस्ट" के लिए कोई पिछड़ा नहीं है ... लेकिन peirix या searlea द्वारा बताए गए लूप के लिए "जबकि" के साथ "लूप" की स्थिति के रूप में बहुत अधिक होना चाहिए। और यह सबसे तेज लूप का परीक्षण किया गया था।
स्वेनफिनके

11
दिलचस्प है, लेकिन मुझे आश्चर्य है कि अगर प्री-डेक्रिमेंट और भी तेज होगा। चूंकि यह मैं के मध्यवर्ती मूल्य को स्टोर करने के लिए नहीं होगा।
टवनफॉसन

यदि मुझे सही याद है, तो मेरे हार्डवेयर-कोर्स प्रोफ ने कहा, कि 0 या नहीं 0 के लिए परीक्षण सबसे तेजी से संभव है "गणना"। जबकि (i--) परीक्षण हमेशा 0 के लिए एक परीक्षा है। शायद इसीलिए यह सबसे तेज है?
स्टैम्पडेक्स

3
@tvanfosson यदि आप पूर्व में कमी करते हैं --iतो आपको var current = arr[i-1];लूप के अंदर उपयोग करना होगा या यह एक बार बंद हो जाएगा ...
डेवलेगर

2
मैंने सुना है कि i---i की तुलना में तेज़ हो सकता है क्योंकि दूसरे मामले में प्रोसेसर को घटाना और फिर नए मूल्य के खिलाफ परीक्षण करना होगा (निर्देशों के बीच एक डेटा निर्भरता है), जबकि पहले मामले में प्रोसेसर कर सकता है मौजूदा मूल्य का परीक्षण करें और कुछ समय बाद मूल्य घटाएं। मुझे यकीन नहीं है कि यह जावास्क्रिप्ट पर लागू होता है या केवल बहुत ही निम्न-स्तरीय सी कोड है।
marcus

128

मैं इस जवाब के साथ एक व्यापक तस्वीर देने की कोशिश करता हूं।

कोष्ठक में निम्नलिखित विचार मेरा विश्वास था जब तक कि मैंने अभी हाल ही में इस मुद्दे का परीक्षण नहीं किया है:

[[ निम्न स्तर की भाषाओं के संदर्भ में जैसे C / C ++ , कोड को संकलित किया जाता है ताकि प्रोसेसर के पास एक विशेष सशर्त कूद कमांड हो जब एक चर शून्य (या गैर-शून्य) हो।
इसके अलावा, यदि आप इस अधिक अनुकूलन की परवाह करते हैं, तो आप इसके ++iबजाय जा सकते हैं i++, क्योंकि ++iएक ही प्रोसेसर कमांड है जबकि i++इसका मतलब है j=i+1, i=j।]

वास्तव में तेजी से छोरों को अनियंत्रित करके किया जा सकता है:

for(i=800000;i>0;--i)
    do_it(i);

यह जिस तरह से धीमी हो सकती है

for(i=800000;i>0;i-=8)
{
    do_it(i); do_it(i-1); do_it(i-2); ... do_it(i-7);
}

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

के अनुसार उच्च स्तरीय भाषाओं के , जावास्क्रिप्ट के अनुसार , जैसा कि आपने पूछा, यदि आप पुस्तकालयों पर भरोसा करते हैं, तो आप लूपिंग के लिए अंतर्निहित कार्यों का अनुकूलन कर सकते हैं। उन्हें यह तय करने दें कि यह कैसे किया जाता है।

नतीजतन, जावास्क्रिप्ट में, मैं कुछ का उपयोग करने का सुझाव दूंगा

array.forEach(function(i) {
    do_it(i);
});

यह कम त्रुटि वाला भी है और ब्राउज़रों के पास आपके कोड को अनुकूलित करने का मौका है।

[टिप्पणी: न केवल ब्राउज़र, बल्कि आपके पास आसानी से अनुकूलन करने के लिए एक स्थान है, बस forEachफ़ंक्शन (ब्राउज़र पर निर्भरता से) को फिर से परिभाषित करें ताकि यह नवीनतम सर्वश्रेष्ठ प्रवंचना का उपयोग करे! :) @AMK विशेष मामलों में कहते हैं कि यह उपयोग करने के बजाय array.popया इसके लायक है array.shift। यदि आप ऐसा करते हैं, तो इसे पर्दे के पीछे रखें। अत्यंत overkill विकल्प जोड़ने के लिए करने के लिए है forEachपाशन एल्गोरिथ्म चयन करने के लिए।]

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

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

मैंने थोड़ी और जांच की और यह पता चला कि C / C ++ में, यहां तक ​​कि 5e9 = (50,000x100,000) के संचालन के लिए, ऊपर और नीचे जाने के बीच कोई अंतर नहीं है यदि परीक्षण स्थिरांक के खिलाफ किया जाता है जैसे @alestanis कहते हैं। (JsPerf परिणाम कभी-कभी असंगत होते हैं, लेकिन बड़े और एक ही कहते हैं: आप एक बड़ा बदलाव नहीं कर सकते।)
इसलिए --iऐसा होना "पॉश" बात है। यह केवल आपको एक बेहतर प्रोग्रामर जैसा दिखता है। :)

दूसरी ओर, इस 5e9 स्थिति में अनियंत्रित होने के कारण, इसने मुझे 12 सेकंड से 2.5 सेकंड तक नीचे ला दिया, जब मैं 10 के दशक में चला गया, और 2.1 सेकंड में जब मैं 20 के दशक में चला गया। यह अनुकूलन के बिना था, और अनुकूलन ने बहुत कम समय के लिए चीजों को अप्राप्य बना दिया है। :) (मेरी तरह से ऊपर या उपयोग करने पर अनियंत्रित किया जा सकता है i++, लेकिन यह जावास्क्रिप्ट में आगे की चीजें नहीं लाता है।)

सभी में: नौकरी के साक्षात्कार के लिए i--/ i++और ++i/ i++अंतर रखें , array.forEachउपलब्ध होने पर या अन्य जटिल पुस्तकालय कार्यों के लिए छड़ी । ;)


18
मुख्य शब्द "हो सकता है"। आपका अनियंत्रित लूप भी मूल की तुलना में धीमा हो सकता है। अनुकूलन करते समय, हमेशा मापें ताकि आपको पता चले कि आपके परिवर्तनों का क्या प्रभाव पड़ा है।
जुल्फ

1
@ जैलफ वास्तव में, +1। दक्षता में विभिन्न लूपिंग लैंथ (> = 1) अलग हैं। यही कारण है कि यदि संभव हो तो पुस्तकालयों में इस नौकरी को छोड़ना अधिक सुविधाजनक है, यह उल्लेख करने के लिए नहीं कि ब्राउज़र विभिन्न आर्किटेक्चर पर चलते हैं इसलिए यह बेहतर हो सकता है यदि वे तय करें कि कैसे करना है array.each(...)। मुझे नहीं लगता कि वे कोशिश करेंगे और लूप के लिए अनलॉन्डिंग प्लेन के साथ प्रयोग करेंगे।
बार्नी

1
@BarnabasSzabolcs सवाल विशेष रूप से जावास्क्रिप्ट के लिए है, सी या अन्य भाषाओं के लिए नहीं। जे एस में वहाँ है एक अंतर, कृपया नीचे मेरा उत्तर देखें। हालांकि प्रश्न पर लागू नहीं, +1 अच्छा उत्तर!
AMK

1
और वहाँ तुम जाओ - +1एक Great Answerबैज पाने के लिए । वाकई शानदार जवाब। :)
रोहित जैन

1
APL / A ++ कोई भाषा नहीं है। लोग इनका उपयोग यह व्यक्त करने के लिए करते हैं कि उन्हें भाषा की बारीकियों में कोई दिलचस्पी नहीं है, लेकिन कुछ ऐसा जो उन भाषाओं को साझा करते हैं।
बार्नी

33

i-- जितना तेज है i++

नीचे दिया गया यह कोड आपके जितना ही तेज़ है, लेकिन एक अतिरिक्त चर का उपयोग करता है:

var up = Things.length;
for (var i = 0; i < up; i++) {
    Things[i]
};

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


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

एक सामान्य दुभाषिया / संकलक के अनुकूलक द्वारा अतिरिक्त चर को समाप्त कर दिया जाएगा। बिंदु '0 से तुलना' है - आगे के स्पष्टीकरण के लिए मेरा उत्तर देखें।
एच।-डर्क श्मिट

1
लूप के अंदर 'अप' लगाने के लिए बेहतर है:for (var i=0, up=Things.length; i<up; i++) {}
थंडरन

22

चूंकि, आप इस विषय में रुचि रखते हैं, तो एक जावास्क्रिप्ट लूप बेंचमार्क के बारे में ग्रेग रेइमर की वेबलॉग पोस्ट पर नज़र डालें, जावास्क्रिप्ट में कोड लूप का सबसे तेज़ तरीका क्या है? :

मैंने जावास्क्रिप्ट में कोडिंग लूप के विभिन्न तरीकों के लिए एक लूप बेंचमार्किंग टेस्ट सूट बनाया। इनमें से कुछ पहले से ही हैं, लेकिन मुझे ऐसा कोई नहीं मिला जिसने देशी सरणियों और HTML संग्रह के बीच अंतर को स्वीकार किया हो।

आप एक लूप पर एक प्रदर्शन परीक्षण भी खोलकर कर सकते हैं https://blogs.oracle.com/greimer/resource/loop-test.html(काम नहीं करता है यदि जावास्क्रिप्ट ब्राउज़र में अवरुद्ध है, उदाहरण के लिए, NoScript )।

संपादित करें:

मिलान एडमोवस्की द्वारा बनाया गया एक और हालिया बेंचमार्क यहां रन-टाइम में प्रदर्शन किया जा सकता है अलग अलग ब्राउज़रों के लिए है।

मैक ओएस एक्स 10.6 पर फ़ायरफ़ॉक्स 17.0 में एक परीक्षण के लिए मुझे निम्नलिखित लूप मिला:

var i, result = 0;
for (i = steps - 1; i; i--) {
  result += i;
}

के रूप में सबसे तेजी से पहले:

var result = 0;
for (var i = steps - 1; i >= 0; i--) {
  result += i;
}

बेंचमार्क उदाहरण।


5
वह पद अभी 4 वर्ष का है। जिस गति (जावास्क्रिप्ट!) को जावास्क्रिप्ट इंजनों को अद्यतन किया गया है, उसे देखते हुए, अधिकांश सलाह अप्रचलित होने की संभावना है - लेख में क्रोम का उल्लेख भी नहीं है और यह चमकदार V8 जावास्क्रिप्ट इंजन है।
यी जियांग

हां, मुझे वह विस्तार याद आ रहा है, जो इस ओर इशारा करने के लिए धन्यवाद। उस समय के लिए लूप पर --i या i ++ के बीच बहुत अंतर नहीं था।
ड्रीमक्रैश

19

यह --या नहीं है ++, यह तुलना ऑपरेशन है। साथ --आप उपयोग कर सकते हैं एक 0 के साथ तुलना करें, जबकि साथ ++आप लंबाई के साथ तुलना की जरूरत है। प्रोसेसर पर, शून्य के साथ तुलना सामान्य रूप से उपलब्ध है, जबकि एक परिमित पूर्णांक के साथ तुलना करने के लिए एक घटाव की आवश्यकता होती है।

a++ < length

वास्तव में संकलित है

a++
test (a-length)

इसलिए संकलित होने पर प्रोसेसर पर अधिक समय लगता है।


15

संक्षिप्त जवाब

सामान्य कोड के लिए, विशेष रूप से उच्च स्तरीय भाषा जैसे कि जावास्क्रिप्ट में , कोई प्रदर्शन अंतर नहीं है i++और i--

प्रदर्शन मानदंड forलूप और तुलना में उपयोग है कथन है।

यह सभी उच्च स्तरीय भाषाओं पर लागू होता है और ज्यादातर जावास्क्रिप्ट के उपयोग से स्वतंत्र होता है। स्पष्टीकरण नीचे पंक्ति में परिणामी कोडांतरक कोड है।

विस्तृत विवरण

लूप में एक प्रदर्शन अंतर हो सकता है। पृष्ठभूमि यह है कि कोडांतरक कोड स्तर पर आप देख सकते हैं कि एcompare with 0 सिर्फ एक बयान है जिसे अतिरिक्त रजिस्टर की आवश्यकता नहीं है।

यह तुलना लूप के प्रत्येक पास पर जारी की जाती है और परिणामस्वरूप औसत दर्जे का प्रदर्शन में सुधार हो सकता है।

for(var i = array.length; i--; )

इस तरह एक छद्म कोड के लिए मूल्यांकन किया जाएगा :

 i=array.length
 :LOOP_START
 decrement i
 if [ i = 0 ] goto :LOOP_END
 ... BODY_CODE
 :LOOP_END

ध्यान दें कि 0 एक शाब्दिक, या दूसरे शब्दों में, एक स्थिर मूल्य है।

for(var i = 0 ; i < array.length; i++ )

इस तरह एक छद्म कोड के लिए मूल्यांकन किया जाएगा (सामान्य दुभाषिया अनुकूलन माना जाता है):

 end=array.length
 i=0
 :LOOP_START
 if [ i < end ] goto :LOOP_END
 increment i
 ... BODY_CODE
 :LOOP_END

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

बस मेरे 5 सेंट

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

आम तौर पर सरणी शुरू से अंत तक क्लासिक पुनरावृत्ति बेहतर है।

संभवतः अवांछित उलट अनुक्रम में परिणाम शुरू करने के लिए सरणी अंत से तेज पुनरावृत्ति ।

स्क्रिप्टम के बाद

जैसा कि एक टिप्पणी में पूछा गया है: के मूल्यांकन में --iऔर i--का अंतर हैi पहले या बाद के ।

सबसे अच्छा विवरण इसे आज़माने के लिए है ;-) यहाँ एक बैश उदाहरण है।

 % i=10; echo "$((--i)) --> $i"
 9 --> 9
 % i=10; echo "$((i--)) --> $i"
 10 --> 9

1
1+ अच्छी व्याख्या। बस एक सवाल थोड़ा गुंजाइश से बाहर है, क्या आप कृपया --iऔर i--इसके बीच के अंतर को भी समझा सकते हैं ?
अफशीन मेहरबानी

14

मैंने सब्बल टेक्स्ट 2 में भी यही सिफारिश देखी है।

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

उदाहरण के लिए,

for (var i = 0; i < document.getElementsByTagName('li').length; i++)

की तुलना में बहुत धीमी है

for (var i = 0, len = document.getElementsByTagName('li').length; i < len; i++)

जहां से मैं खड़ा हूं, आपके प्रश्न में फॉर्म में मुख्य सुधार यह है कि यह एक अतिरिक्त चर घोषित नहीं करता है ( lenमेरे उदाहरण में)

लेकिन अगर आप मुझसे पूछें, तो पूरा बिंदु i++बनाम i--अनुकूलन के बारे में नहीं है, बल्कि प्रत्येक पुनरावृत्ति पर सरणी की लंबाई का मूल्यांकन नहीं करने के बारे में है (आप jsperf पर एक बेंचमार्क परीक्षण देख सकते हैं )।


1
मुझे यहाँ गणना करने वाले शब्द को छोड़ना होगापावेल के जवाब पर मेरी टिप्पणी देखें । ECMA कल्पना बताती है कि जब आप इसे संदर्भित करते हैं तो सरणी की लंबाई की गणना नहीं की जाती है।
कोजिरो

क्या 'मूल्यांकन' एक बेहतर विकल्प होगा? दिलचस्प तथ्य btw, मुझे नहीं पता था कि
BBog

एक सामान्य दुभाषिया / संकलक के अनुकूलक द्वारा अतिरिक्त चर को समाप्त कर दिया जाएगा। वही array.length मूल्यांकन पर लागू होता है। बिंदु '0 से तुलना' है - आगे के स्पष्टीकरण के लिए मेरा जवाब देखें।
एच।-डर्क श्मिट

@ H.-DirkSchmitt अपने सामान्य ज्ञान के उत्तर को एक तरफ रखकर, जावास्क्रिप्ट के इतिहास में लंबे समय तक संकलक ने लुकअप चेन की प्रदर्शन लागत को दूर नहीं किया । AFAIK V8 पहली बार प्रयास करने वाला था।
कोजिरो

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

13

मुझे नहीं लगता है कि यह कहना है कि जावास्क्रिप्ट में i--तेजी से है कि समझ में आता है i++

सबसे पहले , यह पूरी तरह से जावास्क्रिप्ट इंजन कार्यान्वयन पर निर्भर करता है।

दूसरे , बशर्ते कि सरलतम JIT'ed का निर्माण करता है और मूल निर्देशों का अनुवाद किया जाता है, तो i++बनाम i--पूरी तरह से इसे निष्पादित करने वाले सीपीयू पर निर्भर करेगा। यानी, ARMs (मोबाइल फोन) पर यह घटने के बाद 0 से नीचे जाने के लिए तेज़ है और शून्य की तुलना करने पर इसे एक ही निर्देश में निष्पादित किया जाता है।

शायद, आपने सोचा था कि एक दूसरे की तुलना में तेज़ था क्योंकि सुझाया गया तरीका है

for(var i = array.length; i--; )

लेकिन सुझाव दिया रास्ता नहीं है क्योंकि एक तेजी से तो दूसरे, लेकिन सिर्फ इसलिए कि अगर आप लिखते हैं

for(var i = 0; i < array.length; i++)

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

दूसरा कारण, i--"तेज" क्यों माना जा सकता है क्योंकि लूप को नियंत्रित करने के लिए जावास्क्रिप्ट इंजन को केवल एक आंतरिक चर को आवंटित करने की आवश्यकता है (चर को var i)। यदि आप array.length या किसी अन्य वैरिएबल से तुलना करते हैं तो लूप को नियंत्रित करने के लिए एक से अधिक आंतरिक वैरिएबल होना चाहिए, और आंतरिक चर की संख्या एक जावास्क्रिप्ट इंजन की सीमित संपत्ति है। कम चर का उपयोग लूप में किया जाता है और JIT के पास अनुकूलन के लिए अधिक मौका है। इसीलिए i--तेजी से विचार किया जा सकता है ...


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

ठीक है, यह सुनिश्चित नहीं है कि एक्मा कल्पना क्या कहती है, लेकिन विभिन्न जावास्क्रिप्ट इंजनों के कुछ आंतरिक के बारे में जानना आसान नहीं है getLength(){return m_length; }क्योंकि इसमें कुछ हाउसकीपिंग शामिल है। लेकिन अगर आप पीछे की ओर सोचने की कोशिश करते हैं: जो सरणी कार्यान्वयन को लिखने के लिए काफी सरल होगा जहां लंबाई की वास्तव में गणना करने की आवश्यकता होगी :)
पावेल पी

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

मैं जो कहना चाह रहा हूं वह यह है कि यदि आप इसके बारे में सोचते हैं तो युक्ति का उल्लंघन करना बहुत कठिन है।
पावेल पी

1
x86 उस संबंध में एआरएम की तरह है। dec/jnzबनाम inc eax / cmp eax, edx / jne
पीटर कॉर्ड्स

13

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

तो, यहाँ तुम जाओ:

सरल उत्तर: i-- आम तौर पर तेजी से होता है क्योंकि इसे हर बार चलने पर 0 की तुलना करने की आवश्यकता नहीं होती है, विभिन्न तरीकों पर परीक्षा परिणाम नीचे हैं:

परीक्षण के परिणाम: इस jsPerf द्वारा "सिद्ध" के रूप में , arr.pop()वास्तव में अब तक का सबसे तेज लूप है। लेकिन, पर ध्यान केंद्रित कर --i, i--, i++और ++iके रूप में आप अपने प्रश्न में पूछा गया तो यहाँ jsPerf कर रहे हैं (वे कई jsPerf के से हैं, कृपया नीचे सूत्रों देखें) परिणाम में संक्षेप:

--iऔर i--फ़ायरफ़ॉक्स में समान हैं जबकि i--क्रोम में तेज है।

Chrome में लूप के लिए एक बेसिक ( for (var i = 0; i < arr.length; i++)) फ़ायरफ़ॉक्स की तुलना में अधिक तेज़ है i--और --iधीमा है।

क्रोम और फ़ायरफ़ॉक्स दोनों में, एक कैश arr.lengthकिया गया क्रोम लगभग 170,000 ऑप्स / सेक से आगे है।

एक महत्वपूर्ण अंतर के बिना, अधिकांश ब्राउज़रों में ++iतेजी से i++होता है, AFAIK, यह कभी भी किसी भी ब्राउज़र में दूसरा तरीका नहीं है।

छोटा सारांश: arr.pop() अब तक का सबसे तेज लूप है; विशेष रूप से उल्लिखित लूप के लिए, i--सबसे तेज़ लूप है।

स्रोत: http://jsperf.com/fastest-array-loops-in-javascript/15 , http://jsperf.com/ipp-vs-ppi-2

हम उम्मीद करते है कि यह आपके सवाल का जवाब दे देगा।


1
ऐसा लगता है कि आपका popपरीक्षण केवल इतना तेज़ लगता है क्योंकि यह लूप के बहुमत के लिए सरणी आकार को 0 तक कम कर रहा है - जहां तक ​​मैं बता सकता हूं। हालांकि यह सुनिश्चित करने के लिए कि चीजें उचित हैं, मैंने इस jsperf को उसी तरह से बनाया है ताकि प्रत्येक टेस्ट के साथ उसी तरह से सरणी बनाई जा सके - जो .shift()कि मेरे कुछ ब्राउज़रों के लिए विजेता होने के रूप में दिखाई देता है - न कि मुझे क्या उम्मीद होगी हालांकि :) jsperf.com / तुलना-विभिन्न प्रकार के-लूपिंग
पेब्बल

उल्लिखित होने के लिए केवल एक ++i
उपविभाजित

12

यह आपके एरे को मेमोरी में रखने और मेमोरी पेज के हिट अनुपात पर निर्भर करता है, जबकि आप उस एरे को एक्सेस कर रहे हैं।

कुछ मामलों में कॉलम क्रम में सरणी सदस्यों तक पहुंचना हिट अनुपात में वृद्धि के कारण पंक्ति क्रम से तेज है।


1
यदि केवल ओपी ने पूछा था कि एक ही मैट्रिक्स को अलग-अलग क्रमों में
ट्रेस करने के

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

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

10

पिछली बार जब मैंने इसके बारे में परेशान किया था, जब वह 6502 असेंबली (8-बिट, हाँ!) लिख रहा था । बड़ा लाभ यह है कि अधिकांश अंकगणित संचालन (विशेष रूप से डिक्रीमेंट) ने झंडे का एक सेट अपडेट किया, उनमें से एक था Z, 'शून्य पर पहुंच गया' संकेतक।

इसलिए, लूप के अंत में आपने सिर्फ दो निर्देश दिए थे: DEC(डीक्रिमेंट) और JNZ(जम्प अगर शून्य नहीं), तो कोई तुलना नहीं!


जावास्क्रिप्ट के मामले में स्पष्ट रूप से यह लागू नहीं होता है (क्योंकि यह सीपीयू पर चलता है जिसमें ऐसे ऑप-कोड नहीं होते हैं)। सबसे अधिक संभावना है कि i--बनाम के पीछे वास्तविक कारण यह i++है कि पूर्व के साथ आप लूप के दायरे में अतिरिक्त नियंत्रण चर का परिचय नहीं देते हैं। नीचे मेरा उत्तर ... देखें
पावेल पी

1
सही, यह वास्तव में लागू नहीं होता है; लेकिन यह एक बहुत ही सामान्य सी शैली है, और यह हम में से उन लोगों को साफ दिखता है जिन्हें इसकी आदत हो गई है। :-)
जेवियर

x86 और ARM दोनों इस लिहाज से 6502 जैसे हैं। dec/jnzइसके बजाय inc/cmp/jnex86 मामले के लिए। आप किसी खाली लूप को किसी भी तेज़ी से नहीं देखेंगे (दोनों ब्रांच थ्रूपुट को संतृप्त करेंगे), लेकिन नीचे की गिनती से लूप ओवरहेड कम हो जाता है। वर्तमान Intel HW प्रीफ़ैचर्स मेमोरी एक्सेस पैटर्न से परेशान नहीं होते हैं जो या तो अवरोही क्रम में जाते हैं। मुझे लगता है कि पुराने सीपीयू 4 बैकवर्ड स्ट्रीम और 6 या 10 फॉरवर्ड स्ट्रीम, IIRC जैसे ट्रैक कर सकते हैं।
पीटर कॉर्ड्स

7

इसे जावास्क्रिप्ट (और सभी भाषाओं) द्वारा समझाया जा सकता है जो अंततः सीपीयू पर चलने के लिए ऑपकोड में बदल जाता है। सीपीयू में हमेशा शून्य के खिलाफ तुलना करने के लिए एक ही निर्देश होता है, जो बहुत तेज़ है।

एक तरफ के रूप में, अगर आप countहमेशा की गारंटी दे सकते हैं, तो आप इसे >= 0सरल बना सकते हैं:

for (var i = count; i--;)
{
  // whatever
}

1
छोटा स्रोत कोड जरूरी नहीं है कि इसका मतलब यह तेजी से होगा। क्या आपने इसे मापा है?
ग्रेग हेविगिल

उफ़ मैं यह एक याद किया। वहाँ सिर पर कील ठोकते हैं।
रॉबर्ट

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

7
@ ब्रायन नॉबलुच: यदि आप "डिक ईएक्सएक्स" (x86 कोड) जैसे किसी निर्देश का उपयोग करते हैं तो वह निर्देश स्वचालित रूप से Z (शून्य) ध्वज को सेट करता है जिसे आप बीच में एक और तुलना निर्देश का उपयोग किए बिना तुरंत परीक्षण कर सकते हैं।
ग्रेग हेविगेल

1
जावास्क्रिप्ट की व्याख्या करते समय, मुझे संदेह है कि opcodes अड़चन होने वाला है, हालांकि। अधिक संभावना है कि कम टोकन का मतलब है दुभाषिया स्रोत कोड को तेजी से संसाधित कर सकता है।
टोड ओवेन

6

यह --या ++संकेत पर निर्भर नहीं है , लेकिन यह उन परिस्थितियों पर निर्भर करता है जो आप लूप में लागू करते हैं।

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

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


कई नैनोसेकेंड लूप्स सेकंड बन सकते हैं ... जब आपके पास इसके लिए समय हो तो इसे अनुकूलित करना कभी भी बुरा नहीं है
Buksy

6

for(var i = array.length; i--; )ज्यादा तेज नहीं है। लेकिन जब आप के array.lengthसाथ प्रतिस्थापित करते हैं super_puper_function(), तो यह काफी हो सकता है तेजी से (क्योंकि यह हर चरण में कहा जाता है)। यही अंतर है।

यदि आप इसे 2014 में बदलने जा रहे हैं, तो आपको अनुकूलन के बारे में सोचने की आवश्यकता नहीं है। यदि आप इसे "खोज और बदलें" के साथ बदलने जा रहे हैं, तो आपको अनुकूलन के बारे में सोचने की आवश्यकता नहीं है। यदि आपके पास कोई समय नहीं है, तो आपको अनुकूलन के बारे में सोचने की आवश्यकता नहीं है। लेकिन अब, आपको इसके बारे में सोचने का समय मिल गया है।

पुनश्च: i--से तेज नहीं है i++


6

इसे छोटा करने के लिए: जावास्क्रिप्ट में ऐसा करने में कोई अंतर नहीं है।

सबसे पहले, आप इसे स्वयं परख सकते हैं:

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

जहां तक ​​आप देख सकते हैं, किसी भी वातावरण में प्रदर्शन के बीच कोई अंतर नहीं है।

यदि आप अपनी स्क्रिप्ट के प्रदर्शन में सुधार करना चाहते हैं, तो वे चीजें जो आप करने की कोशिश कर सकते हैं:

  1. एक var a = array.length;बयान है ताकि आप लूप में हर बार इसके मूल्य की गणना नहीं करेंगे
  2. लूप को अनियंत्रित करें http://en.wikipedia.org/wiki/Loop_unwinding

लेकिन आपको यह समझना होगा कि जो सुधार आप प्राप्त कर सकते हैं वह इतना महत्वहीन होगा, कि ज्यादातर आपको इसकी परवाह भी नहीं करनी चाहिए।

मेरी अपनी राय क्यों इस तरह की गलत धारणा (Dec vs Inc) सामने आई

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

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


दिलचस्प है, लेकिन आपके परीक्षण के लिए win7 पर फ़ायरफ़ॉक्स 16.0.2 के तहत चल रहा है, वेतन वृद्धि लूप 29% धीमा था ... संपादित करें: कि अवहेलना। बार-बार किए गए परीक्षण अनिर्णायक साबित हुए, बाद के रनों के लिए मेरे परीक्षा परिणामों में आश्चर्यजनक मात्रा में शोर है। यकीन नहीं होता क्यों।

हाँ, मैंने इसके लिए और सब कुछ बंद करके और केवल परीक्षण चलाने की कोशिश की। फिर भी जीत मिली। बहुत अजीब।

मुझे लगता है कि आप वास्तविक बिंदु को याद करते हैं कि शून्य पर जाना जावास्क्रिप्ट में बेहतर क्यों माना जाता है। यह ज्यादातर इसलिए है क्योंकि इस तरह केवल एक चर नियंत्रण लूप निष्पादन, इस तरह अनुकूलक / JITer सुधार के लिए अधिक जगह है। array.lengthजरूरी नहीं कि प्रदर्शन के लिए दंड का उपयोग किया जाए, बस इसलिए कि जेएस वर्चुअल मशीन यह पता लगाने के लिए पर्याप्त स्मार्ट है कि सरणी को लूप के शरीर द्वारा संशोधित नहीं किया गया है। नीचे मेरा जवाब देखें।
पावेल पी

1
नाइटपैकिंग: यह प्राचीन तथ्य (असेंबली लैंग्वेज ऑप्टिमाइजेशन) अप्रचलित नहीं है , बस आर्कन है। जब तक आप वास्तव में ऐसा करने की जरूरत नहीं है। :-)
जेवियर

6

मैंने jsbench पर तुलना की

जैसा कि एलस्टानी ने कहा, एक चीज जो आरोही छोरों में समय लेती है, वह है, प्रत्येक पुनरावृत्ति के लिए, आपके सरणी के आकार का मूल्यांकन करना। इस लूप में:

for ( var i = 1; i <= array.length; i++ )

.lengthहर बार जब आप वेतन वृद्धि का मूल्यांकन करते हैं i। इस एक में:

for ( var i = 1, l = array.length; i <= l; i++ )

आप .lengthकेवल एक बार मूल्यांकन करते हैं, जब आप घोषणा करते हैं i। इस एक में:

for ( var i = array.length; i--; )

तुलना निहित है, यह घटने से ठीक पहले होती है i, और कोड बहुत पठनीय है। हालांकि, जो एक भयानक अंतर बना सकता है, वह यह है कि आपने लूप के अंदर क्या रखा है।

फ़ंक्शन के लिए कॉल के साथ लूप (परिभाषित कहीं और):

for (i = values.length; i-- ;) {
  add( values[i] );
}

इनकोड कोड के साथ लूप:

var sum = 0;
for ( i = values.length; i-- ;) {
  sum += values[i];
}

यदि आप अपने कोड को इनलाइन कर सकते हैं, तो एक फ़ंक्शन को कॉल करने के बजाय, सुपाठ्यता का त्याग किए बिना, आप एक लूप को परिमाण का क्रम तेज कर सकते हैं!


नोट : चूंकि ब्राउज़र सरल कार्यों को बेहतर बनाने में अच्छा हो रहा है , यह वास्तव में इस बात पर निर्भर करता है कि आपका कोड कितना जटिल है। इसलिए, अनुकूलन करने से पहले प्रोफ़ाइल, क्योंकि

  1. अड़चन कहीं और हो सकती है (अजाक्स, रिफ्लो, ...)
  2. आप एक बेहतर एल्गोरिदम चुन सकते हैं
  3. आप एक बेहतर डेटा संरचना चुन सकते हैं

लेकिन याद रखें:

कोड लोगों को पढ़ने के लिए लिखा जाता है, और केवल संयोगवश मशीनों के निष्पादन के लिए।


इस उत्तर के लिए और बेंचमार्क के लिए +1। मैंने forEach परीक्षण जोड़ दिए हैं और बेंचमार्क को एक ब्राउज़र में रन करने योग्य फ़ाइल के साथ-साथ नोड में भी फिर से काम किया है। jsfiddle.net/hta69may/2 । नोड के लिए "रिवर्स लूप, निहित तुलना, इनबिल्ट कोड" सबसे तेज है। लेकिन एफएफ 50 में परीक्षण ने उत्सुक परिणाम दिखाए: न केवल समय लगभग 10 गुना कम (!) था, लेकिन दोनों "फॉरएच" परीक्षण "रिवर्स लूप" के रूप में तेज़ थे। शायद नोड दोस्तों को V8 के बजाय मोज़िला के JS इंजन का उपयोग करना चाहिए? :)
Fr0sT

4

जिस तरह से आप अब कर रहे हैं वह तेज़ नहीं है (इसके अलावा यह एक अनिश्चित लूप होने के नाते, मुझे लगता है कि आप करने का मतलब है i--

यदि आप इसे तेजी से करना चाहते हैं:

for (i = 10; i--;) {
    //super fast loop
}

बेशक आप इसे इतने छोटे लूप पर नोटिस नहीं करेंगे। इसका कारण यह है कि आप तेजी से घट रहे हैं क्योंकि मैं जाँच कर रहा हूं कि यह "सच" है (यह 0 तक पहुंचने पर "झूठे" का मूल्यांकन करता है)


1
क्या आप एक अर्ध-उपनिवेश याद कर रहे हैं? (i = 10; i--;)
searlea

हां हां, मैंने गलती तय की। और अगर हम picky होने जा रहे हैं तो मैं आपको इंगित करूँगा कि आप अपने i - के बाद अपने अर्ध कोलन को भूल गए! हे।
djdd87

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

4
हां, मैंने इसे मापा है, और मैं यह नहीं कह रहा हूं कि छोटा स्रोत इसे तेज बनाता है, लेकिन कम संचालन इसे तेज बनाता है।
peirix

4

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

लूप के लिए कोडिंग का पारंपरिक तरीका इस तरह से एक सरणी लिस को संसाधित करने के लिए:

for (var i = 0; i < myArray.length; i++) {...

इसके साथ समस्या यह है कि myArray.length का उपयोग करके सरणी की लंबाई का मूल्यांकन करने में समय लगता है और जिस तरह से हमने लूप को कोड किया है इसका मतलब है कि इस मूल्यांकन को लूप के चारों ओर हर बार किया जाना है। यदि सरणी में 1000 तत्व हैं तो सरणी की लंबाई का मूल्यांकन 1001 बार किया जाएगा। यदि हम रेडियो बटन देख रहे थे और myForm.myButtons.length थी, तो मूल्यांकन में और भी अधिक समय लगेगा क्योंकि निर्दिष्ट प्रपत्र के भीतर बटन के उपयुक्त समूह को सबसे पहले लूप के चारों ओर हर बार लंबाई का मूल्यांकन करने से पहले स्थित होना चाहिए।

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

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

ऐसा करने के लिए कोड है:

for (var i = 0, var j = myArray.length; i < j; i++) {...

तो अब हम केवल एक बार सरणी के आकार का मूल्यांकन करते हैं और लूप के चारों ओर हर बार उस मान को रखने वाले वेरिएबल के खिलाफ अपने लूप काउंटर का परीक्षण करते हैं। यह अतिरिक्त चर सरणी के आकार का मूल्यांकन करने की तुलना में बहुत तेज़ी से पहुँचा जा सकता है और इसलिए हमारा कोड पहले की तुलना में बहुत तेज़ी से चलेगा। हमारे पास हमारी स्क्रिप्ट में केवल एक अतिरिक्त चर है।

अक्सर यह मायने नहीं रखता है कि हम सरणी को किस क्रम में संसाधित करते हैं जब तक कि सरणी में सभी प्रविष्टियाँ संसाधित नहीं हो जातीं। जहां यह मामला है, हम अतिरिक्त कोड के साथ दूर करके अपने कोड को थोड़ा तेज कर सकते हैं जो हमने अभी जोड़ा है और रिवर्स ऑर्डर में सरणी को संसाधित कर रहे हैं।

अंतिम कोड जो हमारे सरणी को सबसे कुशल तरीके से संसाधित करता है, वह है:

for (var i = myArray.length-1; i > -1; i--) {...

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


4

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

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

आपको पठनीय कोड लिखना चाहिए।


3

कई मामलों में, इसका अनिवार्य रूप से इस तथ्य से कोई लेना-देना नहीं है कि प्रोसेसर अन्य तुलनाओं की तुलना में तेजी से शून्य कर सकते हैं।

इसका कारण केवल कुछ जावास्क्रिप्ट इंजन हैं (जेआईटी सूची में शामिल) वास्तव में मशीन भाषा कोड उत्पन्न करते हैं।

अधिकांश जावास्क्रिप्ट इंजन स्रोत कोड के आंतरिक प्रतिनिधित्व का निर्माण करते हैं जो वे तब व्याख्या करते हैं (यह पता लगाने के लिए कि यह कैसा है, फ़ायरफ़ॉक्स के स्पाइडरमॉन्की पर इस पृष्ठ के नीचे एक नज़र डालें )। आम तौर पर अगर कोड का एक टुकड़ा व्यावहारिक रूप से एक ही काम करता है लेकिन एक सरल आंतरिक प्रतिनिधित्व की ओर जाता है, तो यह तेजी से चलेगा।

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


3

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

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

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


रिकॉर्ड के लिए, DJNZ8051 (z80) ISA में एक निर्देश है। x86 में dec/jnzइसके बजाय है inc/cmp/jne, और जाहिरा तौर पर हाथ में कुछ समान है। मुझे पूरा यकीन है कि यह जावास्क्रिप्ट अंतर का कारण नहीं है; यह लूप की स्थिति में और अधिक विकसित होने से है।
पीटर कॉर्ड्स

3

इसका उपयोग किया कहा था कि - I तेज था (C ++ में) क्योंकि केवल एक परिणाम है, डीग्रेडेड मूल्य। i-- को वापस मूल्य में घटे हुए मूल्य को स्टोर करने की आवश्यकता है और परिणाम के रूप में मूल मूल्य को भी बनाए रखें (j = i--)। अधिकांश कंपाइलरों में यह एक के बजाय दो रजिस्टरों का उपयोग करता है जिसके कारण एक अन्य चर को रजिस्टर चर के रूप में बनाए रखने के बजाय मेमोरी में लिखा जा सकता है।

मैं उन अन्य लोगों से सहमत हूं जिन्होंने कहा है कि इससे इन दिनों कोई फर्क नहीं पड़ता है।


3

बहुत ही सरल शब्दों में

"- मैं और मैं ++। वास्तव में, वे दोनों एक ही समय लेते हैं"।

लेकिन इस मामले में जब आपके पास वृद्धिशील ऑपरेशन होता है .. प्रोसेसर हर बार चर का मूल्यांकन करता है। 1 से घटता है और घटने के मामले में .. विशेष रूप से इस मामले में, यह मूल्यांकन करेगा। केवल 1 प्राप्त होने तक।


3

सबसे पहले, i++और i--जावास्क्रिप्ट सहित किसी भी प्रोग्रामिंग भाषा पर ठीक उसी समय लें।

निम्नलिखित कोड बहुत अलग समय लेते हैं।

तेज:

for (var i = 0, len = Things.length - 1; i <= len; i++) { Things[i] };

धीरे:

for (var i = 0; i <= Things.length - 1; i++) { Things[i] };

इसलिए निम्नलिखित कोड अलग समय भी लेते हैं।

तेज:

for (var i = Things.length - 1; i >= 0; i--) { Things[i] };

धीरे:

for (var i = 0; i <= Things.length - 1; i++) { Things[i] };

कंपाइलर के ऑप्टिमाइज़ेशन की वजह से PS स्लो केवल कुछ भाषाओं (जावास्क्रिप्ट इंजन) के लिए धीमा है। सबसे अच्छा तरीका है का उपयोग करना है '<' के बजाय '<=' (या '=') और '--i' के बजाय 'i--'


3

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

पाश के लिए इस तरह से चलाता है:

  • शुरुआत में एक बार वैरिएबल को प्रारंभ करें।
  • पाश की दूसरी संकार्य में बाधा की जाँच करें, <, >, <=, आदि
  • फिर लूप लगा दें।
  • लूप में वृद्धि और लूप फिर से इन प्रक्रियाओं को फिर से फेंक देते हैं।

इसलिए,

for (var i = Things.length - 1; i >= 0; i--) {
    Things[i]
}; 

शुरुआत में केवल एक बार सरणी लंबाई की गणना करेगा और यह बहुत समय नहीं है, लेकिन

for(var i = array.length; i--; ) 

प्रत्येक लूप में लंबाई की गणना करेगा, इसलिए यह बहुत समय का उपभोग करेगा।


var i = Things.length - 1; i >= 0; i--1 बार भी लंबाई की गणना करेगा।
दिमित्री

1
मुझे यकीन नहीं है कि " --ऑपरेशन 2 के पूरक" का क्या अर्थ होगा, लेकिन मुझे लगता है कि इसका मतलब है कि यह कुछ को नकार देगा। नहीं, यह किसी भी वास्तुकला पर कुछ भी नकारात्मक नहीं करता है। 1 को घटाना 1 को जोड़ने के समान सरल है, आप बस एक सर्किट बनाते हैं जो कि वहन करने के बजाय उधार लेता है।
ओलाथे

1

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

उत्तर संभवतः इस बात पर निर्भर करेगा कि आप किस ब्राउज़र का उपयोग कर रहे हैं। कुछ के पास दूसरों की तुलना में अलग परिणाम होंगे।


4
यह अभी भी उसके सवाल का जवाब नहीं देगा कि यह क्यों तेज है। उसे बस एक बेंचमार्क मिलेगा, बिना किसी जानकारी के कि वह इस तरह से क्यों है ...
peirix

3
यह सच है। हालांकि, प्रत्येक ब्राउज़र में प्रत्येक जावास्क्रिप्ट इंजन के सटीक कार्यान्वयन को जानने के बिना, "क्यों" भाग का उत्तर देना लगभग असंभव है। यहाँ बहुत सारे उत्तर "पूर्व-निर्धारण के बजाय पूर्वनिर्धारण का उपयोग करें" (एक C ++ ऑप्टिमाइज़ेशन ट्रिक) और "शून्य के खिलाफ तुलना" जैसी वास्तविक उपाधियों के इर्द-गिर्द फेंके गए हैं जो देशी कोड संकलित भाषाओं में सही हो सकते हैं लेकिन जावास्क्रिप्ट नंगे धातु से बहुत दूर है। सी पी यू।
ग्रेग हेविगेल

4
-1 मैं पूरी तरह से असहमत हूं कि सबसे अच्छा तरीका यह कोशिश करना है। कुछ उदाहरण सामूहिक ज्ञान को प्रतिस्थापित नहीं करते हैं, और यह एक जैसे मंचों का पूरा बिंदु है।
क्रिस्टोफ़

1

इसे प्यार करो, बहुत सारे निशान हैं लेकिन कोई जवाब नहीं: डी

बस शून्य के खिलाफ एक तुलना रखो हमेशा सबसे तेज तुलना होती है

इसलिए (a == 0) वास्तव में सच की तुलना में जल्दी है (a == 5)

यह छोटा और तुच्छ है और एक संग्रह में 100 मिलियन पंक्तियों के साथ यह औसत दर्जे का है।

यानी लूप अप पर आप कह सकते हैं कि मैं कहाँ <= array.length और मैं वेतन वृद्धि कर रहा हूँ

एक डाउन लूप पर आप कह सकते हैं कि मैं कहां = = 0 और इसके बजाय मैं घट रहा हूं।

तुलना तेज है। पाश की 'दिशा' नहीं।


जैसा कि कहा गया है कि इस सवाल का कोई जवाब नहीं है क्योंकि जावास्क्रिप्ट इंजन सभी अलग-अलग होते हैं और इसका जवाब इस बात पर निर्भर करता है कि आप इसे किस ब्राउज़र पर माप रहे हैं।
ग्रेग हेवगिल

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

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

1

मदद करने वालों की मदद से एक हेडहेड --- वोट इस उत्तर !!!

इस पृष्ठ पर सबसे लोकप्रिय उत्तर फ़ायरफ़ॉक्स 14 के लिए काम नहीं करता है और jsLinter पास नहीं करता है। "जबकि" छोरों को एक तुलना ऑपरेटर की आवश्यकता होती है, असाइनमेंट की नहीं। यह क्रोम, सफारी और यहां तक ​​कि काम करता है। लेकिन फायरफॉक्स में दम तोड़ देता है।

यह टूट गया है!

var i = arr.length; //or 10
while(i--)
{
  //...
}

यह काम करेगा! (फ़ायरफ़ॉक्स पर काम करता है, jsLinter गुजरता है)

var i = arr.length; //or 10
while(i>-1)
{
  //...
  i = i - 1;
}

2
मैंने इसे फ़ायरफ़ॉक्स 14 पर आज़माया और यह ठीक काम किया। मैंने उत्पादन पुस्तकालयों के उदाहरण देखे हैं while (i--)जो कई ब्राउज़रों में काम करते हैं और काम करते हैं। क्या आपके परीक्षण के बारे में कुछ अजीब हो सकता था? क्या आप फ़ायरफ़ॉक्स 14 के बीटा संस्करण का उपयोग कर रहे थे?
मैट ब्राउन

1

यह सिर्फ एक अनुमान है, लेकिन शायद यह इसलिए है क्योंकि प्रोसेसर के लिए किसी अन्य मान के बजाय 0 (i> = 0) के साथ तुलना करना आसान है (i <Things.length)।


3
+1 मत करें कि अन्य लोगों ने मतदान नहीं किया है। हालाँकि .length का बार-बार मूल्यांकन वास्तविक inc / decrement की तुलना में बहुत अधिक समस्या है, लूप एंड के लिए चेक महत्वपूर्ण हो सकता है। मेरा सी कंपाइलर लूप्स पर कुछ चेतावनी देता है जैसे: "टिप्पणी # 1544-डी: (ULP 13.1) डिटेल लूप की गिनती। सिफारिश करें कि लूप्स की गणना करें क्योंकि जीरो का पता लगाना आसान है"
हॉरर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.