v8 जावास्क्रिप्ट का प्रदर्शन निहितार्थ का संकेत, चलो, और var?


90

कार्यात्मक अंतरों के बावजूद, नए कीवर्ड 'लेट' और 'कॉन्स्ट' का उपयोग करने से 'वर्जन' के सापेक्ष प्रदर्शन पर कोई सामान्य या विशिष्ट प्रभाव पड़ता है?

कार्यक्रम चलाने के बाद:

function timeit(f, N, S) {
    var start, timeTaken;
    var stats = {min: 1e50, max: 0, N: 0, sum: 0, sqsum: 0};
    var i;
    for (i = 0; i < S; ++i) {
        start = Date.now();
        f(N);
        timeTaken = Date.now() - start;

        stats.min = Math.min(timeTaken, stats.min);
        stats.max = Math.max(timeTaken, stats.max);
        stats.sum += timeTaken;
        stats.sqsum += timeTaken * timeTaken;
        stats.N++
    }

    var mean = stats.sum / stats.N;
    var sqmean = stats.sqsum / stats.N;

    return {min: stats.min, max: stats.max, mean: mean, spread: Math.sqrt(sqmean - mean * mean)};
}

var variable1 = 10;
var variable2 = 10;
var variable3 = 10;
var variable4 = 10;
var variable5 = 10;
var variable6 = 10;
var variable7 = 10;
var variable8 = 10;
var variable9 = 10;
var variable10 = 10;

function varAccess(N) {
    var i, sum;
    for (i = 0; i < N; ++i) {
        sum += variable1;
        sum += variable2;
        sum += variable3;
        sum += variable4;
        sum += variable5;
        sum += variable6;
        sum += variable7;
        sum += variable8;
        sum += variable9;
        sum += variable10;
    }
    return sum;
}

const constant1 = 10;
const constant2 = 10;
const constant3 = 10;
const constant4 = 10;
const constant5 = 10;
const constant6 = 10;
const constant7 = 10;
const constant8 = 10;
const constant9 = 10;
const constant10 = 10;

function constAccess(N) {
    var i, sum;
    for (i = 0; i < N; ++i) {
        sum += constant1;
        sum += constant2;
        sum += constant3;
        sum += constant4;
        sum += constant5;
        sum += constant6;
        sum += constant7;
        sum += constant8;
        sum += constant9;
        sum += constant10;
    }
    return sum;
}


function control(N) {
    var i, sum;
    for (i = 0; i < N; ++i) {
        sum += 10;
        sum += 10;
        sum += 10;
        sum += 10;
        sum += 10;
        sum += 10;
        sum += 10;
        sum += 10;
        sum += 10;
        sum += 10;
    }
    return sum;
}

console.log("ctl = " + JSON.stringify(timeit(control, 10000000, 50)));
console.log("con = " + JSON.stringify(timeit(constAccess, 10000000, 50)));
console.log("var = " + JSON.stringify(timeit(varAccess, 10000000, 50)));

.. मेरे परिणाम निम्नलिखित थे:

ctl = {"min":101,"max":117,"mean":108.34,"spread":4.145407097016924}
con = {"min":107,"max":572,"mean":435.7,"spread":169.4998820058587}
var = {"min":103,"max":608,"mean":439.82,"spread":176.44417700791374}

हालाँकि, जैसा कि यहां उल्लेख किया गया है कि कुछ विशेष परिदृश्यों के तहत प्रदर्शन के अंतर की वास्तविक क्षमता का संकेत मिलता है: https://esdiscuss.org/topic/performance-concern-with-let-const


मुझे लगता है कि उपयोग पर निर्भर करता है, उदाहरण के लिए letब्लॉक स्कोप में इस्तेमाल किया जाना चाहिए var, जो ब्लॉक स्कोप नहीं है, लेकिन केवल कार्यक्षेत्र है।
एडीनो

अगर मैं पूछ सकता हूं, तो वह @adeneo क्यों है?
शॉन 2078

1
@ sean2078 - यदि आपको एक वैरिएबल घोषित करने की आवश्यकता है जो केवल एक ब्लॉक स्कोप में रहता है, letतो वह ऐसा करेगा, और फिर कचरा एकत्र किया जाएगा, जबकि var, जो कार्य स्कूप किया गया है, neccessarely उसी तरह से काम नहीं करेगा। फिर से मुझे लगता है, यह उपयोग के लिए इतना विशिष्ट है, कि दोनों letऔर अधिक प्रदर्शनकारी हो const सकते हैं, लेकिन हमेशा नहीं रहेंगे।
एडीनो

1
मैं इस उलझन में हूं कि उद्धृत कोड किसी भी अंतर को कैसे प्रदर्शित करता है varऔर let: यह कभी भी उपयोग नहीं करता है let
टीजे क्राउडर

1
वर्तमान में यह नहीं है - केवल const बनाम var .. मूल रूप से gist.github.com/srikumarks/1431640 (srikumarks के लिए क्रेडिट) से सॉर्ट किया गया है, हालांकि कोड को प्रश्न में खींचने का अनुरोध किया गया था
sean2078

जवाबों:


123

टी एल; डॉ

सिद्धांत रूप में , इस लूप का एक अपनाने वाला संस्करण:

for (let i = 0; i < 500; ++i) {
    doSomethingWith(i);
}

के साथ एक ही पाश की एक unoptimized संस्करण की तुलना में धीमी हो सकती है var:

for (var i = 0; i < 500; ++i) {
    doSomethingWith(i);
}

क्योंकि प्रत्येक लूप पुनरावृत्ति के लिए एक अलग i चर बनाया जाता है let, जबकि केवल एक iही होता है var

इसके खिलाफ तर्क देना तथ्य यह है varकि फहराया गया है, इसलिए इसे लूप के बाहर घोषित किया गया है, जबकि letइसे केवल लूप के भीतर घोषित किया गया है, जो एक अनुकूलन लाभ प्रदान कर सकता है।

व्यवहार में , 2018 में, आधुनिक जावास्क्रिप्ट इंजन लूप का पर्याप्त आत्मनिरीक्षण करते हैं ताकि यह पता चल सके कि यह उस अंतर को कब अनुकूलित कर सकता है। (इससे पहले भी, ऑड्स हैं आपका लूप पर्याप्त काम कर रहा था कि अतिरिक्त- letसंबंधित ओवरहेड को वैसे भी धोया गया था। लेकिन अब आपको इसके बारे में चिंता करने की ज़रूरत नहीं है।)

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

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

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

विवरण

varऔर letएक forलूप में महत्वपूर्ण अंतर यह है कि iप्रत्येक पुनरावृत्ति के लिए एक अलग बनाया जाता है; यह क्लासिक "पाश में बंद" समस्या को संबोधित करता है:

प्रत्येक लूप बॉडी ( विशेष लिंक ) के लिए नया एन्वायरमेंटRecord बनाना काम है, और काम में समय लगता है, यही वजह है कि सिद्धांत में letसंस्करण की तुलना में धीमी हैvar संस्करण ।

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

यहां 2018 में, ऐसा लगता है कि V8 (और फ़ायरफ़ॉक्स में स्पाइडरमॉन्की) पर्याप्त आत्मनिरीक्षण कर रहा है कि लूप में कोई प्रदर्शन लागत नहीं है जो कि let's' वेरिएबल-प्रति-इट्रेशन प्रति शब्दार्थ का उपयोग नहीं करता है । इस परीक्षण को देखें ।


कुछ मामलों में, constअच्छी तरह से अनुकूलन के लिए एक अवसर प्रदान कर सकता हैvar विशेष रूप से वैश्विक चर के लिए नहीं होगा।

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

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

Objects याद रखें कि वस्तुओं के साथ, मूल्य वस्तु का संदर्भ है, वस्तु का नहीं। इसलिए const o = {}, आप वस्तु की स्थिति को बदल सकते हैं ( o.answer = 42), लेकिन आप oएक नई वस्तु की ओर संकेत नहीं कर सकते (क्योंकि इसमें ऑब्जेक्ट के संदर्भ को बदलने की आवश्यकता होगी)।


उपयोग करते समय letया constअन्य var-समान स्थितियों में, उनके अलग-अलग प्रदर्शन होने की संभावना नहीं होती है। इस फ़ंक्शन का ठीक वैसा ही प्रदर्शन होना चाहिए, जैसे आप उपयोग करते हैं varया let, उदाहरण के लिए:

function foo() {
    var i = 0;
    while (Math.random() < 0.5) {
        ++i;
    }
    return i;
}

यह सब, ज़ाहिर है, कोई फर्क नहीं पड़ता है और कुछ के बारे में चिंता करने के लिए केवल तभी और जब हल करने के लिए एक वास्तविक समस्या है।


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

@ sean2078: मैं letलूप उदाहरण में भी उपयोग करता हूं । प्रदर्शन अंतर केवल 99.999% मामले में इसके बारे में चिंता करने योग्य नहीं है।
टीजे क्राउडर

2
2018 के मध्य तक, लेट और वर्जन वाले संस्करणों की क्रोम में समान गति है, इसलिए अब कोई अंतर नहीं है।
मैक्स

1
@DMM: अच्छी खबर है, अनुकूलन कम से कम V8 और स्पाइडरमोंकी में पकड़ा गया प्रतीत होता है। :-)
टीजे क्राउडर

1
धन्यवाद। काफी उचित।
हाइपर

20

"LET" LOOP घोषणाओं में बेहतर है

इस तरह के नाविक में एक साधारण परीक्षण (5 बार) के साथ:

// WITH VAR
console.time("var-time")
for(var i = 0; i < 500000; i++){}
console.timeEnd("var-time")

निष्पादित करने का औसत समय 2.5ms से अधिक है

// WITH LET
console.time("let-time")
for(let i = 0; i < 500000; i++){}
console.timeEnd("let-time")

निष्पादित करने का औसत समय 1.5ms से अधिक है

मैंने पाया कि लूप का समय बेहतर है।


7
फ़ायरफ़ॉक्स 65.0 में इस चल रहा है, मैं का मतलब गति मिल गया var=138.8msऔर let=4ms। यह टाइपो नहीं है, letअभी 30 गुना से अधिक तेज है
कटमरी

7
मैं सिर्फ नोड v12.5 में यह परीक्षण किया। मैंने पाया कि गति गति var=2.6msऔर हैं let=1.0ms। तो चलो Node दो बार की तुलना में थोड़ा अधिक तेज है।
केन हूपर

2
बस सामान्य बिंदु बनाने के लिए कि प्रदर्शन परीक्षण ऑप्टिमाइज़र की उपस्थिति में कठिन है: मुझे लगता है कि लेट लूप को पूरी तरह से अनुकूलित किया जा रहा है - केवल ब्लॉक के अंदर मौजूद है और लूप का कोई साइड इफेक्ट नहीं है और यह जानने के लिए V8 काफी स्मार्ट है। बस ब्लॉक को हटा दें, फिर लूप। var घोषणा को फहराया जाता है ताकि यह पता न चल सके। आपके छोरों के रूप में वे मुझे 1ms / 0.4ms मिल रहे हैं, हालांकि अगर दोनों के लिए मेरे पास लूप के बाहर एक चर जम्मू (var या let) है जो कि वेतन वृद्धि भी है, तो मुझे 1ms / 1.5ms मिलता है। यानी वार् लूप नहीं बदले, लूप को अब अधिक समय दें।
यूआन स्मिथ

@KaneHooper - अगर आपको फ़ायरफ़ॉक्स में पांच गुना अंतर मिला है, तो यह खाली लूप बॉडी होगी जो उसने किया था। असली छोरों में खाली शरीर नहीं होते हैं।
टीजे क्राउडर

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

10

टीजे क्राउडर का जवाब बहुत बढ़िया है।

यहाँ एक अतिरिक्त है: "मैं अपने रुपये के लिए मौजूदा धमाके की घोषणा को कास्ट करने के लिए सबसे अधिक धमाका कब करूंगा?"

मैंने पाया है कि सबसे अधिक प्रदर्शन को बढ़ावा "निर्यात" कार्यों के साथ करना था।

इसलिए यदि फ़ाइल A, B, R, और Z फाइल यू में एक "यूटिलिटी" फ़ंक्शन पर कॉल कर रहे हैं जो आमतौर पर आपके ऐप के माध्यम से उपयोग किया जाता है, तो उस यूटिलिटी फ़ंक्शन को "कॉन्स्ट" पर स्विच करना और एक कॉन्स्टेंट को पैरेंट फ़ाइल संदर्भ को eak कर सकते हैं कुछ बेहतर प्रदर्शन किया। यह मेरे लिए लग रहा था कि यह औसत रूप से तेज़ नहीं था, लेकिन मेरे सकल अखंड फ्रेंकस्टीन-एड ऐप के लिए समग्र मेमोरी खपत लगभग 1-3% कम हो गई थी। यदि आप क्लाउड या अपने नंगे सर्वर पर नकदी के बैग खर्च कर रहे हैं, तो कंघी करने के लिए 30 मिनट खर्च करने और उन वार्ताओं की कुछ घोषणाओं को अपडेट करने का एक अच्छा कारण हो सकता है।

मुझे लगता है कि यदि आप पढ़ते हैं कि कैसे कॉन्स्ट, वर्, और कवर के तहत काम करते हैं तो आप शायद पहले ही ऊपर निष्कर्ष निकाल चुके हैं ... लेकिन अगर आप इस पर "नज़र" रखते हैं: डी।

जब मैं अद्यतन कर रहा था, तो नोड v8.12.0 पर बेंचमार्किंग से मुझे जो याद आया, उससे मेरा ऐप ~ 240 एमबी रैम से ~ 233 एमबी रैम तक बेकार हो गया।


3

टीजे क्राउडर का जवाब बहुत अच्छा है लेकिन:

  1. 'लेट' कोड को अधिक पठनीय बनाने के लिए किया जाता है, अधिक शक्तिशाली नहीं
  2. सिद्धांत रूप में चलो var की तुलना में धीमी हो जाएगी
  3. अभ्यास से संकलक पूरी तरह से (स्थैतिक विश्लेषण) एक अपूर्ण कार्यक्रम को हल नहीं कर सकता है इसलिए कुछ समय में यह अनुकूलन को याद करेगा
  4. किसी भी स्थिति में 'लेट' का उपयोग करते हुए आत्मनिरीक्षण के लिए अधिक सीपीयू की आवश्यकता होगी, जब Google v8 को पार्स करना शुरू होता है तो बेंच को शुरू किया जाना चाहिए
  5. अगर आत्मनिरीक्षण विफल हो जाता है 'चलो' वी 8 कचरा कलेक्टर पर जोर देगा, तो इसे खाली / पुन: उपयोग करने के लिए अधिक पुनरावृत्ति की आवश्यकता होगी। यह अधिक रैम की खपत करेगा। पीठ को इन बिंदुओं को ध्यान में रखना चाहिए
  6. Google क्लोजर बदल देगा var में ...

वर्जन और लेट के बीच परफॉरमेंस गैप का असर रियल-लाइफ कम्प्लीट प्रोग्राम में देखा जा सकता है न कि सिंगल बेसिक लूप पर।

वैसे भी, यह बताने के लिए कि आपको कहां नहीं करना है, आपके कोड को कम पठनीय बनाता है।


आउट ऑफ इंटरेस्ट- किस सिद्धांत से letधीमा है var? विशेष रूप से उपरोक्त उत्तर पर टिप्पणियों में आम सहमति को देखते हुए यह तेज है?
जेम्स दिवावेद
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.