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% की सभी बुराई की जड़ है। ब्लूबर्ड बहुत बार बहुत कुछ बुनियादी करता है इसलिए यह इन प्रदर्शन हैक से बहुत लाभ प्राप्त करता है - कॉलबैक के रूप में तेज़ होना आसान नहीं है। आपको शायद ही कभी ऐसा कुछ कोड में करना पड़ता है जो पुस्तकालय को शक्ति नहीं देता है।