JavaScript की परफ़ॉर्मेंस के रहस्यों को सुलझाने के लिए, फ़ोरेंसिक और डिटेक्टिव प्रोसेस का इस्तेमाल करें

शुरुआती जानकारी

हाल ही के सालों में, वेब ऐप्लिकेशन में बहुत तेज़ी आई है. कई ऐप्लिकेशन अब इतनी तेज़ी से चलते हैं कि कुछ डेवलपर को चिंता हुई कि "क्या वेब काफ़ी तेज़ है?". कुछ ऐप्लिकेशन के लिए यह ज़रूरी हो सकता है कि हालांकि, अच्छी परफ़ॉर्मेंस वाले ऐप्लिकेशन पर काम करने वाले डेवलपर के लिए, यह स्पीड काफ़ी तेज़ नहीं है. JavaScript वर्चुअल मशीन टेक्नोलॉजी में हुए शानदार बदलावों के बावजूद, एक हाल ही के एक अध्ययन से पता चला है कि Google के ऐप्लिकेशन V8 में अपना 50% से 70% समय खर्च करते हैं. आपका ऐप्लिकेशन सीमित समय के लिए उपलब्ध है. एक सिस्टम से इस ऐप्लिकेशन को हटाने का मतलब है कि दूसरा सिस्टम ज़्यादा काम कर सकता है. याद रखें, 60fps पर चल रहे ऐप्लिकेशन में हर फ़्रेम के लिए सिर्फ़ 16 मि॰से॰ या फिर - जैंक. JavaScript और प्रोफ़ाइल JavaScript ऐप्लिकेशन को ऑप्टिमाइज़ करने के बारे में जानने के लिए, Find Your Way to Oz में परफ़ॉर्मेंस की धुंधली समस्या को ट्रैक करने वाली V8 टीम के परफ़ॉर्मेंस डिटेक्टिव की खांचियों की कहानी पढ़ें.

Google I/O 2013 सेशन

मैंने इस सामग्री को Google I/O 2013 में पेश किया था. यह वीडियो देखें:

परफ़ॉर्मेंस मायने क्यों रखती है?

सीपीयू साइकल एक ज़ीरो योग गेम है. अपने सिस्टम के किसी एक हिस्से का कम इस्तेमाल करने से, आप दूसरे हिस्से का ज़्यादा इस्तेमाल कर पाएंगे या ज़्यादा बेहतर तरीके से काम कर पाएंगे. तेज़ी से चलने और ज़्यादा काम करने के लक्ष्य अक्सर प्रतिस्पर्धी होते हैं. उपयोगकर्ता नई सुविधाओं की मांग करते हैं, साथ ही यह भी चाहते हैं कि आपका ऐप्लिकेशन बेहतर तरीके से काम करे. JavaScript वर्चुअल मशीनें तेज़ी से काम करती रहती हैं. हालांकि, कई डेवलपर की तरह, परफ़ॉर्मेंस से जुड़ी समस्याओं को अनदेखा करने की वजह यह नहीं है कि उन्हें मौजूदा वेब ऐप्लिकेशन में परफ़ॉर्मेंस से जुड़ी समस्याओं से निपटने के बारे में पहले से ही पता है. रीयल-टाइम में, हाई फ़्रेम रेट, ऐप्लिकेशन पर जैंक फ़्री होने का दबाव सबसे बड़ी भूमिका है. इनसोम्नियाक गेम ने एक अध्ययन किया, जिससे पता चला कि किसी गेम की सफलता के लिए, एक अच्छा और लंबे समय तक चलने वाला फ़्रेम रेट ज़रूरी है: "एक अच्छा फ़्रेम-रेट अब भी पेशेवर और अच्छी तरह से बनाए गए प्रॉडक्ट की निशानी है." वेब डेवलपर ध्यान देते हैं.

परफ़ॉर्मेंस से जुड़ी समस्याओं को हल करना

परफ़ॉर्मेंस की समस्या को हल करना, किसी अपराध को सुलझाने जैसा है. आपको सबूतों की ध्यान से जांच करनी चाहिए, संदिग्ध वजहों की जांच करनी चाहिए, और अलग-अलग समाधान के साथ प्रयोग करना चाहिए. पूरे तरीके में आपको अपने मापों का रिकॉर्ड रखना होगा, ताकि आप यह पक्का कर सकें कि आपने समस्या को असल में ठीक कर दिया है. इस तरीके और आपराधिक जासूसों के किसी मामले का क्रैक करने के तरीके में बहुत कम अंतर होता है. जासूस सबूतों की जांच करते हैं, संदिग्ध लोगों से पूछताछ करते हैं, और स्मोकिंग बंदूक खोजने के लिए एक्सपेरिमेंट करते हैं.

V8 सीएसआई: ओज़

Find Your Way to Oz बनाने वाले जादूगरों ने V8 टीम के पास एक ऐसी परफ़ॉर्मेंस की समस्या दी है जिसे वे खुद हल नहीं कर सकते थे. कभी-कभी ओज़ फ़्रीज़ हो जाता था, जिससे जैंक हो जाती थी. Oz डेवलपर ने Chrome DevTools में टाइमलाइन पैनल का इस्तेमाल करके, कुछ शुरुआती जांच की थी. मेमोरी के इस्तेमाल को देखते हुए, उन्हें सॉ टूथ ग्राफ़ का सामना करना पड़ा. हर सेकंड में एक बार कचरा उठाने वाला व्यक्ति 10 एमबी कचरा इकट्ठा करता था और तब कचरा इकट्ठा करने की प्रक्रिया जैंक से रुक जाती थी. Chrome Devtools में टाइमलाइन से लिए गए नीचे दिए गए स्क्रीनशॉट से मिलता-जुलता है:

Devtools टाइमलाइन

V8 जासूस, जैकब और यांग ने इस केस को सुलझा लिया था. क्या हुआ, V8 टीम के जैकब और यांग और ओज़ टीम के बीच काफ़ी आगे और पीछे चला गया. मैंने इस बातचीत को उन अहम इवेंट के बारे में बता दिया है जिनसे इस समस्या को ट्रैक करने में मदद मिली.

सबूत

पहला कदम है शुरुआती सबूतों को इकट्ठा करके उनका अध्ययन करना.

हम किस तरह का ऐप्लिकेशन खोज रहे हैं?

Oz डेमो एक इंटरैक्टिव 3D ऐप्लिकेशन है. इस वजह से, कचरा इकट्ठा होने की वजह से रुकने के समय पर यह कार्रवाई बहुत संवेदनशील होती है. याद रखें, 60fps पर चल रहे एक इंटरैक्टिव ऐप्लिकेशन में सभी JavaScript काम करने के लिए 16 मि॰से॰ होते हैं. साथ ही, इसके लिए कुछ समय Chrome को ग्राफ़िक कॉल प्रोसेस करने और स्क्रीन बनाने के लिए छोड़ देना चाहिए.

ओज़ डबल वैल्यू पर अंकगणित के काफ़ी हिसाब से गणना करता है और WebAudio और WebGL को बार-बार कॉल करता है.

हमें परफ़ॉर्मेंस से जुड़ी किस तरह की समस्या दिख रही है?

हम रुकने की ऐसी जगह देख रहे हैं जिसे फ़्रेम ड्रॉप यानी जैंक कहते हैं. ये रोक कचरा इकट्ठा करने की प्रक्रिया से जुड़ी हुई हैं.

क्या डेवलपर सबसे सही तरीके अपना रहे हैं?

हां, Oz डेवलपर को JavaScript वीएम की परफ़ॉर्मेंस और ऑप्टिमाइज़ेशन की तकनीकों के बारे में अच्छे से पता है. ध्यान दें कि Oz डेवलपर अपनी सोर्स भाषा के तौर पर CoffeeScript का इस्तेमाल कर रहे थे और CoffeeScript कंपाइलर के ज़रिए JavaScript कोड बना रहे थे. इसकी वजह से, Oz डेवलपर के लिखे गए कोड और V8 के इस्तेमाल किए जा रहे कोड के बीच संबंध टूट जाने की वजह से, जांच में कुछ गड़बड़ी हुई. Chrome DevTools अब सोर्स मैप का इस्तेमाल करता है. इससे यह काम और आसान हो जाता था.

कचरा उठाने वाला संगठन क्यों काम करता है?

वीएम की मदद से, डेवलपर के लिए JavaScript की मेमोरी को अपने-आप मैनेज किया जाता है. V8, कचरा इकट्ठा करने के एक सामान्य सिस्टम का इस्तेमाल करता है. इसमें मेमोरी को दो या उससे ज़्यादा generations में बांटा जाता है. युवा पीढ़ी के पास ऐसे ऑब्जेक्ट होते हैं जिन्हें हाल ही में बांटा गया है. अगर कोई ऑब्जेक्ट ज़रूरत के मुताबिक लंबे समय तक जीवित रहता है, तो उसे पुरानी पीढ़ी में ले जाया जाता है.

नई पीढ़ी के लोगों को इकट्ठा करने के मुकाबले, नई पीढ़ी के लोगों को इकट्ठा करने की फ़्रीक्वेंसी बहुत ज़्यादा है. यह डिज़ाइन में है, क्योंकि युवा पीढ़ी का कलेक्शन काफ़ी सस्ता है. आम तौर पर, यह माना जाता है कि युवा पीढ़ी के संग्रह की वजह से बार-बार जीसी रुक जाता है.

वर्शन 8 में, यंग मेमोरी स्पेस को मेमोरी के दो समान साइज़ वाले, सटे हुए ब्लॉक में बांटा जाता है. किसी दिए गए समय में, मेमोरी के इन दोनों ब्लॉक में से सिर्फ़ एक का इस्तेमाल किया जाता है और उसे स्पेस कहा जाता है. स्पेस में मेमोरी बची हुई है. हालांकि, नया ऑब्जेक्ट तय करना सस्ता होता है. टू स्पेस में मौजूद कर्सर को नए ऑब्जेक्ट के लिए ज़रूरी बाइट की संख्या पर आगे ले जाया जाता है. यह तब तक जारी रहेगा, जब तक स्पेस के खत्म नहीं हो जाते. इसके बाद, प्रोग्राम रुक जाता है और डेटा इकट्ठा करना शुरू हो जाता है.

V8 जवान मेमोरी

इस स्थिति में, स्पेस से और स्पेस को आपस में बदल दिया जाता है. अंतरिक्ष में क्या था और अब वह अंतरिक्ष से आ गया है, इसे शुरू से आखिर तक स्कैन किया जाता है. इसके अलावा, जो भी ऑब्जेक्ट अब भी ज़िंदा हैं उन्हें अंतरिक्ष में कॉपी कर दिया जाता है या पुरानी पीढ़ी के हीप में ले जाया जाता है. अगर आपको ज़्यादा जानकारी चाहिए, तो हमारा सुझाव है कि आप चेनी के एल्गोरिदम के बारे में पढ़ें.

आपको यह समझना चाहिए कि जब भी कोई ऑब्जेक्ट किसी दूसरे तरीके से या साफ़ तौर पर (नए, [], या {} को कॉल करके) बांटा जाता है, तब आपका ऐप्लिकेशन कूड़ा संग्रह के काफ़ी करीब पहुंच जाता है और ऐप्लिकेशन का खराब होना रुक जाता है.

क्या इस ऐप्लिकेशन के लिए 10 एमबी/सेकंड का कूड़ा फेंकने की उम्मीद है?

संक्षेप में, नहीं. डेवलपर 10 एमबी/सेकंड की बचत करने की कोई उम्मीद नहीं कर रहा है.

संदिग्ध

जांच का अगला चरण संभावित संदिग्धों का पता लगाना और फिर उन्हें हटाना होता है.

संदिग्ध #1

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

संदिग्ध #2

कंस्ट्रक्टर के बाहर किसी ऑब्जेक्ट के "आकार" में बदलाव करना. ऐसा तब होता है, जब कंस्ट्रक्टर के बाहर के किसी ऑब्जेक्ट में एक नई प्रॉपर्टी जोड़ी जाती है. इससे ऑब्जेक्ट के लिए एक नई छिपी हुई क्लास बनती है. जब ऑप्टिमाइज़ किए गए कोड को यह दिखता है कि यह नई छिपी हुई क्लास है, तो इसे हटा दिया जाएगा. ऑप्टिमाइज़ नहीं किया गया कोड तब तक काम करता रहेगा, जब तक कि कोड को फिर से हॉट की कैटगरी में नहीं रखा जाता और फिर से ऑप्टिमाइज़ नहीं किया जाता. इस डी-ऑप्टिमाइज़ेशन, फिर से ऑप्टिमाइज़ेशन चर्न आउट की वजह से जैंक हो सकता है. हालांकि, इसका मतलब यह नहीं है कि कचरा बहुत ज़्यादा होगा. कोड की बारीकी से जांच करने के बाद, यह पुष्टि हुई कि ऑब्जेक्ट का आकार स्टैटिक था. इसलिए, अनुमान के मुताबिक #2 को हटा दिया गया था.

संदिग्ध #3

ऑप्टिमाइज़ नहीं किए गए कोड में अंकगणित. ऑप्टिमाइज़ न किए गए कोड में, असाइन किए जा रहे असल ऑब्जेक्ट में सभी कंप्यूटेशन के नतीजे दिखते हैं. उदाहरण के लिए, यह स्निपेट:

var a = p * d;
var b = c + 3;
var c = 3.3 * dt;
point.x = a * b * c;

नतीजे के तौर पर, पांच HeapNumber ऑब्जेक्ट बनाए जा रहे हैं. पहले तीन वैरिएबल, a, b, और c के लिए हैं. चौथा, अनाम वैल्यू (a * b) के लिए है और 5वां मान #4 * c के लिए है; 5वां मान आखिरकार Points.x को असाइन किया गया.

ओज़ हर फ़्रेम में हज़ारों ऑपरेशन करता है. अगर इनमें से कोई भी कंप्यूटेशन ऐसे फ़ंक्शन में होता है जिन्हें कभी ऑप्टिमाइज़ नहीं किया जाता, तो वे खराब चीज़ों की वजह हो सकती हैं. ऑप्टिमाइज़ नहीं किए गए सिस्टम में कंप्यूटेशन, अस्थायी नतीजों के लिए भी मेमोरी असाइन करते हैं.

संदिग्ध #4

किसी प्रॉपर्टी में डबल सटीक संख्या स्टोर करना. संख्या को स्टोर करने के लिए एक HeapNumber ऑब्जेक्ट बनाया जाना चाहिए. साथ ही, प्रॉपर्टी को इस नए ऑब्जेक्ट पर ले जाने के लिए बदला जाना चाहिए. प्रॉपर्टी को HeapNumber पर पॉइंट करने से कचरा नहीं पैदा होगा. हालांकि, यह संभव है कि ऑब्जेक्ट प्रॉपर्टी के रूप में कई डबल सटीक संख्याएं स्टोर की जा रही हों. कोड में इस तरह के स्टेटमेंट होते हैं:

sprite.position.x += 0.5 * (dt);

ऑप्टिमाइज़ किए गए कोड में, जब भी x को हाल ही में कंप्यूट किया गया मान असाइन किया जाता है, जो दिखता है, सटीक नहीं लगता है. साथ ही, बिना किसी परेशानी के एक नया HeapNumber ऑब्जेक्ट असाइन किया जाता है, जिससे हम कचरा इकट्ठा करने की प्रक्रिया को रोकने के करीब पहुंच जाते हैं.

ध्यान दें कि टाइप किए गए अरे (या ऐसे रेगुलर अरे जिसमें सिर्फ़ डबल असाइन किए गए हैं) का इस्तेमाल करके, इस समस्या से पूरी तरह बचा जा सकता है. ऐसा इसलिए, क्योंकि दो बार सटीक संख्या के लिए स्टोरेज सिर्फ़ एक बार असाइन किया जाता है और बार-बार वैल्यू बदलने के लिए, नए स्टोरेज की ज़रूरत नहीं होती.

संभावना #4 है.

फ़ोरेंसिक जांच सेवा

इस दौरान, जासूसों के पास दो संदिग्ध संदिग्ध चीज़ें हो सकती हैं: हीप संख्याओं को ऑब्जेक्ट प्रॉपर्टी के रूप में स्टोर करना और बिना ऑप्टिमाइज़ किए गए फ़ंक्शन के अंदर अंकगणित का कैलकुलेशन करना. लैब में जाकर, पक्के तौर पर यह तय करने का समय था कि कौनसा संदिग्ध अपराधी है. ध्यान दें: इस सेक्शन में, असल Oz सोर्स कोड में मिली समस्या को दोबारा जनरेट किया जाएगा. यह प्रोसेस, ओरिजनल कोड से कम के ऑर्डर में की जाती है. इसलिए, इसका जवाब देना आसान होता है.

एक्सपेरिमेंट #1

संदिग्ध #3 की जांच की जा रही है (ऑप्टिमाइज़ नहीं किए गए फ़ंक्शन के अंदर अंकगणित का कैलकुलेशन). V8 JavaScript इंजन में एक लॉग इन सिस्टम पहले से मौजूद होता है. इससे इस बात की अहम जानकारी मिलती है कि आगे क्या हो रहा है.

Chrome के न चलने से शुरू करते समय, फ़्लैग के साथ Chrome को लॉन्च करना:

--no-sandbox --js-flags="--prof --noprof-lazy --log-timer-events"

और फिर Chrome को पूरी तरह से बंद करने से मौजूदा डायरेक्ट्री में एक v8.log फ़ाइल बन जाएगी.

v8.log के कॉन्टेंट को समझने के लिए, आपको v8 का वही वर्शन डाउनलोड करना होगा जिसका इस्तेमाल आपका Chrome कर रहा है (इसके बारे में जांच करें:version) और उसे बनाएं.

v8 बनाने के बाद, टिक प्रोसेसर का इस्तेमाल करके लॉग को प्रोसेस किया जा सकता है:

$ tools/linux-tick-processor /path/to/v8.log

(अपने प्लैटफ़ॉर्म के हिसाब से, Linux के लिए mac या Windows का विकल्प बदलें.) (इस टूल को वर्शन 8 में टॉप लेवल सोर्स डायरेक्ट्री से चलाना ज़रूरी है.)

टिक प्रोसेसर, JavaScript फ़ंक्शन की टेक्स्ट वाली ऐसी टेबल दिखाता है जिसमें सबसे ज़्यादा टिक थे:

[JavaScript]:
ticks  total  nonlib   name
167   61.2%   61.2%  LazyCompile: *opt demo.js:12
 40   14.7%   14.7%  LazyCompile: unopt demo.js:20
 15    5.5%    5.5%  Stub: KeyedLoadElementStub
 13    4.8%    4.8%  Stub: BinaryOpStub_MUL_Alloc_Number+Smi
  6    2.2%    2.2%  Stub: BinaryOpStub_ADD_OverwriteRight_Number+Number
  4    1.5%    1.5%  Stub: KeyedStoreElementStub
  4    1.5%    1.5%  KeyedLoadIC:  {12}
  2    0.7%    0.7%  KeyedStoreIC:  {13}
  1    0.4%    0.4%  LazyCompile: ~main demo.js:30

आप देख सकते हैं कि Demo.js में तीन फ़ंक्शन थे: ऑप्ट, ऑप्ट न करें, और मुख्य. ऑप्टिमाइज़ किए गए फ़ंक्शन के नाम के आगे तारे का निशान (*) होता है. देखें कि फ़ंक्शन ऑप्टिमाइजेशन ऑप्टिमाइज़ किया गया है और नहीं चुना गया है.

प्लॉट-टाइमर-इवेंट, V8 डिटेक्टिव टूल बैग में एक और अहम टूल है. इसे इस तरह से चलाया जा सकता है:

$ tools/plot-timer-event /path/to/v8.log

चलाने के बाद, Time-events.png नाम की एक png फ़ाइल मौजूदा डायरेक्ट्री में सेव होती है. इसे खोलने पर आपको कुछ ऐसा दिखेगा:

टाइमर इवेंट

नीचे ग्राफ़ के अलावा, डेटा पंक्तियों में दिखाया जाता है. X ऐक्सिस, समय (मि॰से॰) होता है. बाईं ओर हर पंक्ति के लिए लेबल होते हैं:

टाइमर इवेंट Y ऐक्सिस

V8.एक्ज़ीक्यूशन पंक्ति में, हर प्रोफ़ाइल टिक के ऊपर काले रंग की वर्टिकल लाइन बनाई गई है, जहां V8 JavaScript कोड को एक्ज़ीक्यूट कर रहा था. V8.GCScavenger की हर प्रोफ़ाइल टिक पर, नीले रंग की वर्टिकल लाइन बनी है, जहां V8 नई जनरेशन वाले कलेक्शन को परफ़ॉर्म कर रहा था. इसी तरह, V8 राज्यों के लिए.

"कोड टाइप किया जा रहा है", सबसे अहम पंक्तियों में से एक है. जब ऑप्टिमाइज़ किया गया कोड एक्ज़ीक्यूट हो रहा है, तब यह हरा होगा. साथ ही, ऑप्टिमाइज़ नहीं किया गया कोड एक्ज़ीक्यूट होने पर लाल और नीले रंग का मिक्स होगा. नीचे दिया गया स्क्रीनशॉट, ऑप्टिमाइज़ किए गए से ऑप्टिमाइज़ नहीं किए गए वर्शन में ट्रांज़िशन और फिर ऑप्टिमाइज़ किए गए कोड पर वापस ट्रांज़िशन की जानकारी दिखाता है:

कोड टाइप का इस्तेमाल किया जा रहा है

सही है, लेकिन तुरंत कभी नहीं, यह लाइन पूरी तरह से हरी होगी. इसका मतलब है कि आपके प्रोग्राम को, ऑप्टिमाइज़ की गई स्थिर स्थिति में बदल दिया गया है. ऑप्टिमाइज़ न किया गया कोड, ऑप्टिमाइज़ किए गए कोड से हमेशा धीमा चलेगा.

अगर आपने इतनी अवधि तक पेमेंट कर लिया है, तो ध्यान दें कि ऐप्लिकेशन की रीफ़ैक्टरिंग में तेज़ी से काम किया जा सकता है, ताकि इसे v8 डीबग शेल में चलाया जा सके: d8. d8 का इस्तेमाल करने पर, आपको टिक-प्रोसेसर और प्लॉट-टाइमर-इवेंट टूल की मदद से, उसे तेज़ी से दोहराने का समय मिलता है. d8 का इस्तेमाल करने का एक और फ़ायदा यह है कि इससे असल समस्या को पहचानना आसान हो जाता है. इससे डेटा में कम शोर होता है.

Oz सोर्स कोड से टाइमर इवेंट प्लॉट को देखने पर, ऑप्टिमाइज़ किए गए से ऑप्टिमाइज़ नहीं किए गए कोड में ट्रांज़िशन दिखाया गया. साथ ही, ऑप्टिमाइज़ नहीं किए गए कोड को एक्ज़ीक्यूट करते समय, नई जनरेशन वाले कई कलेक्शन ट्रिगर हुए. यह नीचे दिए गए स्क्रीनशॉट की तरह है (नोट टाइम को बीच से हटा दिया गया है):

टाइमर इवेंट का प्लॉट

अगर आप ध्यान से देखें, तो दिखेगा कि V8 के JavaScript कोड को लागू करते समय दिखने वाली काली लाइनें, नई जनरेशन के कलेक्शन (नीली रेखाएं) वाले प्रोफ़ाइल टिक टाइम के बराबर नहीं हैं. इससे साफ़ तौर पर पता चलता है कि जब कचरा इकट्ठा किया जा रहा होता है, तब स्क्रिप्ट रुक जाती है.

Oz सोर्स कोड से टिक प्रोसेसर आउटपुट को देखकर, टॉप फ़ंक्शन (updateSprites) को ऑप्टिमाइज़ नहीं किया गया है. दूसरे शब्दों में कहें, तो प्रोग्राम ने जिस फ़ंक्शन में ज़्यादा समय बिताया वह ऑप्टिमाइज़ नहीं किया गया था. इससे पूरा पता चलता है कि संदिग्ध #3 अपराधी है. updatedSprites के सोर्स में ऐसे लूप थे जो इस तरह दिखते हैं:

function updateSprites(dt) {
    for (var sprite in sprites) {
        sprite.position.x += 0.5 * dt;
        // 20 more lines of arithmetic computation.
    }
}

V8 के साथ-साथ उन्हें भी जानते हुए, उन्होंने तुरंत यह समझ लिया कि for-i-in लूप कंस्ट्रक्शन को कभी-कभी V8 से ऑप्टिमाइज़ नहीं किया जाता. दूसरे शब्दों में, अगर किसी फ़ंक्शन में for-i-in लूप कंस्ट्रक्ट मौजूद है, तो हो सकता है कि उसे ऑप्टिमाइज़ न किया जा सके. यह आज का खास मामला है और आने वाले समय में बदल सकता है. इसका मतलब है कि वर्शन 8, आने वाले समय में इस लूप कंस्ट्रक्ट को ऑप्टिमाइज़ कर सकता है. हम V8 जासूस नहीं हैं और हम V8 के बारे में नहीं जानते, क्योंकि हम इसे ठीक से नहीं जानते, इसलिए हम यह कैसे पता लगा सकते हैं कि updateSprites को ऑप्टिमाइज़ क्यों नहीं किया गया?

प्रयोग #2

Chrome को इस फ़्लैग के साथ चलाना:

--js-flags="--trace-deopt --trace-opt-verbose"

ऑप्टिमाइज़ेशन और डी-ऑप्टिमाइज़ेशन डेटा का वर्बोस लॉग दिखाता है. अपडेटSprites के डेटा में खोज करने पर, हमें ये चीज़ें मिली हैं:

[updateSprites के लिए बंद किया गया ऑप्टिमाइज़ेशन, वजह: ForInStatement फ़ास्ट केस नहीं है]

जैसा कि जासूस ने अनुमान लगाया था, 'for-i-in' लूप कंस्ट्रक्शन इसकी वजह थी.

केस बंद हो गया

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

function updateSprite(sprite, dt) {
    sprite.position.x += 0.5 * dt;
    // 20 more lines of arithmetic computation.
}

function updateSprites(dt) {
    for (var sprite in sprites) {
        updateSprite(sprite, dt);
    }
}

अपडेटSprite को ऑप्टिमाइज़ किया जाएगा. इसकी वजह से, HeapNumber ऑब्जेक्ट बहुत कम हो जाएंगे. इस वजह से, GC को बार-बार रुकना नहीं पड़ेगा. नए कोड के साथ वही प्रयोग करके इसकी पुष्टि करना आसान होगा. ध्यान रखने वाले पाठक को पता चलेगा कि डबल नंबर को अब भी प्रॉपर्टी के तौर पर सेव किया जा रहा है. अगर प्रोफ़ाइलिंग से पता चलता है कि यह सही है, तो पोज़िशन को डबल का कलेक्शन या टाइप किए गए डेटा कलेक्शन में बदलने से, बनने वाले ऑब्जेक्ट की संख्या कम हो जाएगी.

आखिरी चैप्टर

Oz डेवलपर यहीं तक नहीं रुके. V8 डिटेक्टिव के साथ शेयर किए गए टूल और तकनीकों की मदद से, उन्होंने कुछ अन्य फ़ंक्शन को ढूंढा. साथ ही, वे सिस्टम में अपने हिसाब से सुधार नहीं कर पा रहे थे. साथ ही, उन्होंने कंप्यूटेशन कोड को लीफ़ फ़ंक्शन में शामिल किया, जो ऑप्टिमाइज़ किए गए थे, जिससे परफ़ॉर्मेंस भी बेहतर हुई.

आगे बढ़ें और परफ़ॉर्मेंस से जुड़े कुछ अपराधों को हल करना शुरू करें!