क्या अनाम कार्यों का उपयोग प्रदर्शन को प्रभावित करता है?


89

मैं सोच रहा था, क्या जावास्क्रिप्ट में नामित कार्यों और अनाम कार्यों का उपयोग करने के बीच एक प्रदर्शन अंतर है?

for (var i = 0; i < 1000; ++i) {
    myObjects[i].onMyEvent = function() {
        // do something
    };
}

बनाम

function myEventHandler() {
    // do something
}

for (var i = 0; i < 1000; ++i) {
    myObjects[i].onMyEvent = myEventHandler;
}

यह पहली बार है क्योंकि यह शायद ही कभी इस्तेमाल किए गए कार्यों के साथ आपके कोड को अव्यवस्थित नहीं करता है, लेकिन क्या यह बात है कि आप उस फ़ंक्शन को कई बार पुनः घोषित कर रहे हैं?


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

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

@ निक्की बेशक इसका बहुत पुराना सवाल है, लेकिन नया अपडेटेड जवाब
देखिए

जवाबों:


89

यहां प्रदर्शन समस्या लूप के प्रत्येक पुनरावृत्ति पर एक नई फ़ंक्शन ऑब्जेक्ट बनाने की लागत है और इस तथ्य से नहीं कि आप एक अनाम फ़ंक्शन का उपयोग करते हैं:

for (var i = 0; i < 1000; ++i) {    
    myObjects[i].onMyEvent = function() {
        // do something    
    };
}

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

function myEventHandler() {
    // do something
}

for (var i = 0; i < 1000; ++i) {
    myObjects[i].onMyEvent = myEventHandler;
}

यदि आप लूप में प्रवेश करने से पहले अनाम फ़ंक्शन बनाते थे, तो केवल लूप के अंदर रहते हुए इसे सरणी तत्वों के संदर्भ में असाइन करें, आप पाएंगे कि नामित फ़ंक्शन संस्करण की तुलना में कोई भी प्रदर्शन या शब्दार्थ अंतर नहीं है:

var handler = function() {
    // do something    
};
for (var i = 0; i < 1000; ++i) {    
    myObjects[i].onMyEvent = handler;
}

संक्षेप में, नामित कार्यों पर अनाम का उपयोग करने के लिए कोई अवलोकन योग्य प्रदर्शन लागत नहीं है।

एक तरफ के रूप में, यह ऊपर से दिखाई दे सकता है कि इसमें कोई अंतर नहीं है:

function myEventHandler() { /* ... */ }

तथा:

var myEventHandler = function() { /* ... */ }

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

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


आप फ़ंक्शन बॉडी से पहले कोष्ठक भूल गए। मैंने अभी इसका परीक्षण किया, वे आवश्यक हैं।
चिनोटो वोक्रो

ऐसा लगता है कि बेंचमार्क परिणाम बहुत js- इंजन पर निर्भर हैं!
aleclofabbro

3
जेएस परफेक्ट उदाहरण में कोई दोष नहीं है: केस 1 केवल फ़ंक्शन को परिभाषित करता है, जबकि केस 2 और 3 गलती से फ़ंक्शन को कॉल करते हैं।
bluenote10

तो इस तर्क का उपयोग करते हुए, क्या इसका मतलब है कि node.jsवेब एप्लिकेशन विकसित करते समय , अनुरोध प्रवाह के बाहर फ़ंक्शन बनाना और उन्हें कॉलबैक के रूप में पास करना बेहतर है, अनाम कॉलबैक बनाने की तुलना में?
जेवियर टी मुकोदी

23

यहाँ मेरा परीक्षण कोड है:

var dummyVar;
function test1() {
    for (var i = 0; i < 1000000; ++i) {
        dummyVar = myFunc;
    }
}

function test2() {
    for (var i = 0; i < 1000000; ++i) {
        dummyVar = function() {
            var x = 0;
            x++;
        };
    }
}

function myFunc() {
    var x = 0;
    x++;
}

document.onclick = function() {
    var start = new Date();
    test1();
    var mid = new Date();
    test2();
    var end = new Date();
    alert ("Test 1: " + (mid - start) + "\n Test 2: " + (end - mid));
}

परिणाम:
टेस्ट 1: 142ms टेस्ट 2: 1983ms

ऐसा प्रतीत होता है कि JS इंजन यह नहीं पहचानता कि यह Test2 में समान कार्य है और हर बार इसे संकलित करता है।


3
यह परीक्षण किस ब्राउज़र में आयोजित किया गया था?
औरनिल

5
Chrome 23 पर मेरे लिए समय: (2ms / 17ms), IE9: (20ms / 83ms), FF 17: (2ms / 96ms)
Davy8

आपका जवाब अधिक वजन का हकदार है। इंटेल i5 4570S पर मेरा समय: क्रोम 41 (1/9), IE11 (1/25), FF36 (1/14)। स्पष्ट रूप से एक लूप में अनाम फ़ंक्शन खराब प्रदर्शन करता है।
ThisClark

3
यह परीक्षण उतना उपयोगी नहीं है जितना यह दिखाई देता है। न तो उदाहरण में आंतरिक कार्य वास्तव में निष्पादित किया जा रहा है। प्रभावी रूप से यह सब परीक्षण दिखा रहा है कि एक फ़ंक्शन बनाने से 10000000 गुना तेजी से एक बार होता है।
1

2

एक सामान्य डिजाइन सिद्धांत के रूप में, आपको एक ही कोड को कई बार निहित करने से बचना चाहिए। इसके बजाय आपको एक फ़ंक्शन में कॉमन कोड को बाहर निकालना चाहिए और कई स्थानों से फ़ंक्शन (सामान्य, अच्छी तरह से परीक्षण, संशोधित करने में आसान) को निष्पादित करना चाहिए।

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

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


1

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

यदि आपको कोड को आसान बनाना है, तो प्रदर्शन एक गैर-मुद्दा है जब तक आप फ़ंक्शन को लाखों बार कॉल करने की अपेक्षा नहीं करते हैं।


1

जहां हमारे पास एक प्रदर्शन प्रभाव हो सकता है, फ़ंक्शन घोषित करने के संचालन में है। यहाँ एक फ़ंक्शन के संदर्भ में या बाहर किसी फ़ंक्शन को घोषित करने का एक मानक है:

http://jsperf.com/function-context-benchmark

यदि हम फ़ंक्शन को बाहर घोषित करते हैं, तो क्रोम में ऑपरेशन तेज है, लेकिन फ़ायरफ़ॉक्स में इसके विपरीत है।

अन्य उदाहरण में हम देखते हैं कि यदि आंतरिक फ़ंक्शन शुद्ध कार्य नहीं है, तो फ़ायरफ़ॉक्स में भी प्रदर्शन की कमी होगी: http://jsperf.com/function-context-benchmark-3


0

निश्चित रूप से विभिन्न ब्राउज़रों में, विशेषकर IE ब्राउज़रों में आपका लूप निश्चित रूप से तेज़ होगा, निम्नानुसार लूपिंग कर रहा है:

for (var i = 0, iLength = imgs.length; i < iLength; i++)
{
   // do something
}

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


0

एक संदर्भ लगभग हमेशा धीमा होने वाला है, फिर वह जिस चीज का उल्लेख कर रहा है। इसे इस तरह से सोचें - मान लें कि आप 1 + 1 जोड़ने का परिणाम प्रिंट करना चाहते हैं, जो अधिक समझ में आता है:

alert(1 + 1);

या

a = 1;
b = 1;
alert(a + b);

मुझे लगता है कि यह देखने के लिए एक बहुत ही सरल तरीका है, लेकिन यह सच है, सही है? उदाहरण के लिए, यदि यह कई बार उपयोग होने जा रहा है, तो संदर्भ का उपयोग करें - इनमें से कौन सा उदाहरण अधिक समझ में आता है:

$(a.button1).click(function(){alert('you clicked ' + this);});
$(a.button2).click(function(){alert('you clicked ' + this);});

या

function buttonClickHandler(){alert('you clicked ' + this);}
$(a.button1).click(buttonClickHandler);
$(a.button2).click(buttonClickHandler);

दूसरा एक बेहतर अभ्यास है, भले ही इसे अधिक लाइनें मिलें। उम्मीद है कि यह सब उपयोगी है। (और jquery सिंटैक्स ने किसी को भी नहीं छोड़ा)


0

@nickf

(काश मुझे सिर्फ टिप्पणी करने के लिए प्रतिनिधि होता, लेकिन मैंने केवल इस साइट को पाया है)

मेरा कहना है कि नाम / अनाम कार्यों और निष्पादन में संकलित + संकलन के उपयोग के बीच यहां भ्रम है। जैसा कि मैंने सचित्र किया, आन + नाम का अंतर अपने आप में नगण्य है - मैं कह रहा हूं कि यह उपयोग का मामला है जो दोषपूर्ण है।

यह मेरे लिए स्पष्ट लगता है, लेकिन अगर मुझे नहीं लगता कि सबसे अच्छी सलाह "गूंगा काम न करें" (जिसमें इस उपयोग के मामले की निरंतर ब्लॉक शिफ्टिंग + ऑब्जेक्ट निर्माण एक है) और यदि आप सुनिश्चित नहीं हैं, तो परीक्षण करें!


0

हाँ! अनाम फ़ंक्शंस नियमित कार्यों की तुलना में तेज़ होते हैं। शायद अगर गति का अत्यधिक महत्व है ... कोड पुन: उपयोग से अधिक महत्वपूर्ण है तो अनाम कार्यों का उपयोग करने पर विचार करें।

यहाँ जावास्क्रिप्ट और अनाम कार्यों के अनुकूलन के बारे में एक अच्छा लेख है:

http://dev.opera.com/articles/view/efficient-javascript/?page=2


0

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

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

यह मानते हुए कि आप अच्छी तरह से तैयार किए गए कोड लिख रहे हैं, तो गति के मुद्दों को दुभाषियों / संकलक लिखने वालों की ज़िम्मेदारी होनी चाहिए।


0

@nickf

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

एक और अधिक यथार्थवादी दृष्टिकोण अनाम असाइनमेंट है, वास्तव में आप अपने डॉक्यूमेंट के लिए उपयोग कर रहे हैं। क्लिक्लिक विधि निम्नलिखित की तरह अधिक है, जो वास्तव में हल्के तरीके से एनॉन विधि का पक्षधर है।

आपके लिए एक समान परीक्षण ढांचे का उपयोग करना:


function test(m)
{
    for (var i = 0; i < 1000000; ++i) 
    {
        m();
    }
}

function named() {var x = 0; x++;}

var test1 = named;

var test2 = function() {var x = 0; x++;}

document.onclick = function() {
    var start = new Date();
    test(test1);
    var mid = new Date();
    test(test2);
    var end = new Date();
    alert ("Test 1: " + (mid - start) + "ms\n Test 2: " + (end - mid) + "ms");
}

0

जैसा कि @nickf उत्तर की टिप्पणियों में बताया गया है: इसका उत्तर

एक बार बनाने से एक लाख गुना तेजी से एक समारोह बना रहा है

बस हाँ है। लेकिन जैसा कि उनका जेएस परफेक्ट दिखाता है, यह एक लाख के कारक से धीमा नहीं है, यह दर्शाता है कि यह वास्तव में समय के साथ तेज हो जाता है।

मेरे लिए और अधिक दिलचस्प सवाल है:

कैसे एक दोहराया है + रन बनाने के एक बार + दोहराया बनाने के लिए की तुलना चलाने

यदि कोई फ़ंक्शन एक जटिल संगणना करता है तो फ़ंक्शन ऑब्जेक्ट बनाने के लिए समय सबसे अधिक नगण्य है। लेकिन उन मामलों में सृजन के प्रमुख के बारे में क्या है जहां तेजी से भाग रहे हैं ? उदाहरण के लिए:

// Variant 1: create once
function adder(a, b) {
  return a + b;
}
for (var i = 0; i < 100000; ++i) {
  var x = adder(412, 123);
}

// Variant 2: repeated creation via function statement
for (var i = 0; i < 100000; ++i) {
  function adder(a, b) {
    return a + b;
  }
  var x = adder(412, 123);
}

// Variant 3: repeated creation via function expression
for (var i = 0; i < 100000; ++i) {
  var x = (function(a, b) { return a + b; })(412, 123);
}

यह जेएस परफेक्ट दिखाता है कि केवल एक बार फ़ंक्शन बनाने से उम्मीद के मुताबिक तेज़ी होती है। हालांकि, एक साधारण ऐड की तरह बहुत तेज ऑपरेशन के साथ भी, फंक्शन को बार-बार बनाने का ओवरहेड केवल कुछ प्रतिशत है।

अंतर शायद केवल उन मामलों में महत्वपूर्ण हो जाता है जहां फ़ंक्शन ऑब्जेक्ट बनाना जटिल है, जबकि एक नगण्य रन टाइम बनाए रखने, जैसे, यदि पूरे फ़ंक्शन बॉडी को एक में लपेटा गया है if (unlikelyCondition) { ... }

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