Bluebird का उपयोग कैसे करता है ।toFastProperties फ़ंक्शन किसी ऑब्जेक्ट के गुण "तेज़" बनाता है?


165

ब्लूबर्ड की util.jsफ़ाइल में , इसका निम्न कार्य है:

function toFastProperties(obj) {
    /*jshint -W027*/
    function f() {}
    f.prototype = obj;
    ASSERT("%HasFastProperties", true, obj);
    return f;
    eval(obj);
}

किसी कारण से, वापसी फ़ंक्शन के बाद एक बयान है, जो मुझे यकीन नहीं है कि यह क्यों है।

साथ ही, ऐसा लगता है कि यह जानबूझकर है, क्योंकि लेखक ने इस बारे में JSHint की चेतावनी को चुप करा दिया था:

'वापसी' के बाद अगम्य 'निष्कासन'। (W027)

वास्तव में यह कार्य क्या करता है? क्या util.toFastPropertiesवास्तव में किसी वस्तु के गुण "तेज़" होते हैं?

मैंने स्रोत कोड में किसी भी टिप्पणी या उनके मुद्दों की सूची में स्पष्टीकरण के लिए ब्लूबर्ड के GitHub रिपॉजिटरी के माध्यम से खोज की है, लेकिन मुझे कोई भी नहीं मिला।

जवाबों:


314

2017 अपडेट: पहला, आज आने वाले पाठकों के लिए - यहां एक संस्करण है जो नोड 7 (4+) के साथ काम करता है:

function enforceFastProperties(o) {
    function Sub() {}
    Sub.prototype = o;
    var receiver = new Sub(); // create an instance
    function ic() { return typeof receiver.foo; } // perform access
    ic(); 
    ic();
    return o;
    eval("o" + o); // ensure no dead code elimination
}

एक या दो छोटे अनुकूलन - सभी नीचे अभी भी मान्य है।

आइए पहले चर्चा करते हैं कि यह क्या करता है और यह क्यों तेज है और फिर यह क्यों काम करता है।

यह क्या करता है

V8 इंजन दो ऑब्जेक्ट प्रतिनिधित्व का उपयोग करता है:

  • डिक्शनरी मोड - जिस वस्तु को हैश मैप के रूप में कुंजी - मान मानचित्र में संग्रहीत किया जाता है ।
  • फास्ट मोड - जिसमें ऑब्जेक्ट को स्ट्रक्चर्स की तरह संग्रहित किया जाता है , जिसमें प्रॉपर्टी एक्सेस में कोई संगणना शामिल नहीं होती है।

यहां एक सरल डेमो है जो गति अंतर को दर्शाता है। यहां हम deleteवस्तुओं को धीमी शब्दकोश मोड में बाध्य करने के लिए कथन का उपयोग करते हैं ।

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

इस हैक का उद्देश्य डिक्शनरी को डिक्शनरी मोड से फास्ट मोड में लाना है।

यह तेज क्यों है

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

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

यह काम किस प्रकार करता है

चलो पहले कोड के माध्यम से जाते हैं और यह पता लगाते हैं कि प्रत्येक रेखा क्या करती है:

function toFastProperties(obj) {
    /*jshint -W027*/ // suppress the "unreachable code" error
    function f() {} // declare a new function
    f.prototype = obj; // assign obj as its prototype to trigger the optimization
    // assert the optimization passes to prevent the code from breaking in the
    // future in case this optimization breaks:
    ASSERT("%HasFastProperties", true, obj); // requires the "native syntax" flag
    return f; // return it
    eval(obj); // prevent the function from being optimized through dead code 
               // elimination or further optimizations. This code is never  
               // reached but even using eval in unreachable code causes v8
               // to not optimize functions.
}

हमें स्वयं यह पता लगाने के लिए कोड नहीं है कि v8 यह अनुकूलन करता है, हम इसके बजाय v8 इकाई परीक्षण पढ़ सकते हैं :

// Adding this many properties makes it slow.
assertFalse(%HasFastProperties(proto));
DoProtoMagic(proto, set__proto__);
// Making it a prototype makes it fast again.
assertTrue(%HasFastProperties(proto));

इस परीक्षण को पढ़ना और चलाना हमें दिखाता है कि यह अनुकूलन वास्तव में v8 में काम करता है। हालाँकि - यह देखना अच्छा होगा कि कैसे।

यदि हम objects.ccजांचते हैं कि हम निम्नलिखित फ़ंक्शन (L9925) पा सकते हैं:

void JSObject::OptimizeAsPrototype(Handle<JSObject> object) {
  if (object->IsGlobalObject()) return;

  // Make sure prototypes are fast objects and their maps have the bit set
  // so they remain fast.
  if (!object->HasFastProperties()) {
    MigrateSlowToFast(object, 0);
  }
}

अब, JSObject::MigrateSlowToFastकेवल स्पष्ट रूप से शब्दकोश लेता है और इसे एक तेज V8 ऑब्जेक्ट में परिवर्तित करता है। यह एक सार्थक वाचन है और v8 ऑब्जेक्ट इंटर्नल में एक दिलचस्प अंतर्दृष्टि है - लेकिन यह यहां विषय नहीं है। मैं अब भी गर्मजोशी से सलाह देता हूं कि आप इसे यहां पढ़ें क्योंकि यह v8 वस्तुओं के बारे में जानने का एक अच्छा तरीका है।

यदि हम बाहर की जाँच करते SetPrototypeहैं objects.cc, तो हम देख सकते हैं कि इसे लाइन 12231 में कहा जाता है:

if (value->IsJSObject()) {
    JSObject::OptimizeAsPrototype(Handle<JSObject>::cast(value));
}

जो बदले में कहा जाता है FuntionSetPrototypeजिसके द्वारा हमें मिलता है .prototype =

करना __proto__ =या .setPrototypeOfकरना भी काम किया होगा, लेकिन ये ES6 फ़ंक्शन हैं और नेटस्केप 7 के बाद से सभी ब्राउज़रों पर ब्लूबर्ड चलता है ताकि यहां कोड को सरल बनाने के लिए सवाल से बाहर हो जाए। उदाहरण के लिए, यदि हम देखें तो हम .setPrototypeOfदेख सकते हैं:

// ES6 section 19.1.2.19.
function ObjectSetPrototypeOf(obj, proto) {
  CHECK_OBJECT_COERCIBLE(obj, "Object.setPrototypeOf");

  if (proto !== null && !IS_SPEC_OBJECT(proto)) {
    throw MakeTypeError("proto_object_or_null", [proto]);
  }

  if (IS_SPEC_OBJECT(obj)) {
    %SetPrototype(obj, proto); // MAKE IT FAST
  }

  return obj;
}

जो सीधे है Object:

InstallFunctions($Object, DONT_ENUM, $Array(
...
"setPrototypeOf", ObjectSetPrototypeOf,
...
));

इसलिए - हम नंगे धातु को लिखे गए कोड पेटिका से रास्ता पकड़ चुके हैं। यह अच्छा था।

अस्वीकरण:

याद रखें यह सभी कार्यान्वयन विवरण है। पेटका जैसे लोग अनुकूलन शैतान हैं। हमेशा याद रखें कि समय से पहले अनुकूलन 97% की सभी बुराई की जड़ है। ब्लूबर्ड बहुत बार बहुत कुछ बुनियादी करता है इसलिए यह इन प्रदर्शन हैक से बहुत लाभ प्राप्त करता है - कॉलबैक के रूप में तेज़ होना आसान नहीं है। आपको शायद ही कभी ऐसा कुछ कोड में करना पड़ता है जो पुस्तकालय को शक्ति नहीं देता है।


37
यह सबसे दिलचस्प पोस्ट है जिसे मैंने थोड़ी देर में पढ़ा है। आपके लिए बहुत सम्मान और प्रशंसा!
m59

2
@timoxley मैंने evalकोड के बारे में निम्नलिखित लिखा है (जब कोड ओपी को समझाते हुए कोड टिप्पणी में): "फ़ंक्शन को मृत कोड उन्मूलन या आगे अनुकूलन के माध्यम से अनुकूलित होने से रोकें। यह कोड कभी भी नहीं पहुंचता है, लेकिन अनुपयोगी कोड v8 का अनुकूलन नहीं करने का कारण बनता है। कार्य करता है। " । यहाँ एक संबंधित पढ़ा है । क्या आप मुझे इस विषय पर विस्तार से बताना चाहेंगे?
बेंजामिन ग्रुएनबाम

3
@ डरमैन 1;"डेप्टिमाइजेशन" का कारण नहीं होगा, एक debugger;ने शायद उतना ही अच्छा काम किया होगा। अच्छी बात यह है कि जब evalकुछ ऐसा किया जाता है जो एक तार नहीं है, तो यह उसके साथ कुछ भी नहीं करता है इसलिए यह हानिरहित है - इस तरह काif(false){ debugger; }
बेंजामिन ग्रुएनबाम

6
Btw इस कोड को हाल ही में v8 में बदलाव के कारण अपडेट किया गया है, अब आपको कंस्ट्रक्टर को भी तुरंत करना होगा। तो यह आलसी हो गया; घ
एस्लेइजा

4
@BenjaminGruenbaum क्या आप इस बारे में विस्तार से बता सकते हैं कि इस फ़ंक्शन को अनुकूलित क्यों नहीं किया जाना चाहिए? मिनी कोड में, वैसे भी मौजूद नहीं है। गैर-मिनिमाइज़्ड कोड में यहाँ क्यों उपयोगी है?
बोपौती राजा
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.