ভূমিকা
V8-এ JavaScript-এর পারফর্ম্যান্স উন্নত করার জন্য পরামর্শ এবং উপায় সম্পর্কে ড্যানিয়েল ক্লিফোর্ড Google I/O-তে একটি চমৎকার বক্তৃতা দিয়েছেন। ড্যানিয়েল আমাদের "আরও দ্রুত কাজ করার" জন্য উৎসাহিত করেছিলেন - C++ ও জাভাস্ক্রিপ্টের মধ্যে পারফর্ম্যান্সের পার্থক্য সতর্কভাবে বিশ্লেষণ করা এবং জাভাস্ক্রিপ্ট কীভাবে কাজ করে তা মাথায় রেখে কোড লেখা। ড্যানিয়েলের বক্তব্যের সবচেয়ে গুরুত্বপূর্ণ পয়েন্টগুলির সারসংক্ষেপ এই নিবন্ধে তুলে ধরা হয়েছে এবং পারফর্ম্যান্স সংক্রান্ত নির্দেশিকায় পরিবর্তন হলে আমরা এই নিবন্ধও আপডেট করব।
সবচেয়ে গুরুত্বপূর্ণ পরামর্শ
পারফর্ম্যান্স সংক্রান্ত যেকোনও পরামর্শকে প্রাসঙ্গিক করে তোলা গুরুত্বপূর্ণ। পারফর্ম্যান্স সংক্রান্ত পরামর্শের প্রতি আসক্তি তৈরি হতে পারে এবং কখনও কখনও গভীর পরামর্শের উপর প্রথমে ফোকাস করলে তা আসল সমস্যা থেকে মনোযোগ বিভ্রান্ত করতে পারে। আপনার ওয়েব অ্যাপ্লিকেশনের পারফর্ম্যান্সের সামগ্রিক ভিউ দেখতে হবে - পারফর্ম্যান্স সংক্রান্ত এই পরামর্শে ফোকাস করার আগে, আপনার সম্ভবত PageSpeed-এর মতো টুল দিয়ে কোড বিশ্লেষণ করা এবং স্কোর বাড়ানো উচিত। এটি আপনাকে সময়ের আগেই অপ্টিমাইজ করার সমস্যা এড়াতে সাহায্য করবে।
ওয়েব অ্যাপ্লিকেশনে ভালো পারফর্ম্যান্স পাওয়ার জন্য সেরা প্রাথমিক পরামর্শ হল:
- কোনও সমস্যা হওয়ার (বা লক্ষ্য করার) আগে থেকেই প্রস্তুত থাকুন
- তারপর, আপনার সমস্যার মূল কারণ শনাক্ত করুন ও বুঝুন
- অবশেষে, গুরুত্বপূর্ণ সমস্যার সমাধান করুন
এইসব ধাপ সম্পূর্ণ করতে, V8 কীভাবে JS অপ্টিমাইজ করে তা বোঝা গুরুত্বপূর্ণ হতে পারে, যাতে আপনি JS রানটাইম ডিজাইন মাথায় রেখে কোড লিখতে পারেন। এছাড়াও, উপলভ্য টুল এবং সেগুলি কীভাবে আপনাকে সাহায্য করতে পারে সেই সম্পর্কে জানা গুরুত্বপূর্ণ। ড্যানিয়েল তার বক্তব্যে ডেভেলপার টুল কীভাবে ব্যবহার করতে হয় সেই বিষয়ে আরও ব্যাখ্যা করেছেন; এই ডকুমেন্ট V8 ইঞ্জিন ডিজাইনের সবচেয়ে গুরুত্বপূর্ণ কিছু পয়েন্ট তুলে ধরেছে।
তাহলে, V8 সংক্রান্ত পরামর্শে আসা যাক!
লুকানো ক্লাস
JavaScript-এ কম্পাইল-টাইম টাইপ সংক্রান্ত তথ্য সীমিত: রানটাইমে টাইপ পরিবর্তন করা যেতে পারে, তাই কম্পাইল টাইমে JS টাইপ সম্পর্কে যুক্তি দেওয়া ব্যয়বহুল হবে বলে আশা করা স্বাভাবিক। এর ফলে আপনার মনে প্রশ্ন জাগতে পারে যে জাভাস্ক্রিপ্টের পারফর্ম্যান্স কীভাবে C++-এর কাছাকাছি পৌঁছাতে পারে। তবে, রানটাইমে অবজেক্টের জন্য V8-এর ইন্টার্নালি তৈরি করা হিডেন টাইপ আছে; একই হিডেন ক্লাস সহ অবজেক্ট একই অপ্টিমাইজ করা জেনারেট করা কোড ব্যবহার করতে পারে।
যেমন:
function Point(x, y) {
this.x = x;
this.y = y;
}
var p1 = new Point(11, 22);
var p2 = new Point(33, 44);
// At this point, p1 and p2 have a shared hidden class
p2.z = 55;
// warning! p1 and p2 now have different hidden classes!```
অবজেক্ট ইনস্ট্যান্স p2-তে অতিরিক্ত মেম্বার ".z" যোগ না করা পর্যন্ত, p1 এবং p2-এর ইন্টার্নালি একই হিডেন ক্লাস থাকে - তাই V8, JavaScript কোডের জন্য অপ্টিমাইজ করা অ্যাসেম্বলির একটি একক ভার্সন তৈরি করতে পারে যা p1 বা p2-এর যেকোনও একটিকে ম্যানিপুলেট করে। আপনি যত বেশি হিডেন ক্লাসকে ডাইভার্জ হওয়া থেকে আটকাতে পারবেন, তত বেশি পারফর্ম্যান্স পাবেন।
সুতরাং
- কনস্ট্রাক্টর ফাংশনে সব অবজেক্ট মেম্বার ইনিশিয়ালাইজ করুন (যাতে ইনস্ট্যান্স পরে ধরন পরিবর্তন না করে)
- সবসময় একই ক্রমে অবজেক্ট মেম্বার শুরু করুন
Numbers
ধরন পরিবর্তন করা গেলে, ভি৮ ভ্যালু দক্ষতার সাথে দেখানোর জন্য ট্যাগিং ব্যবহার করে। আপনি যে ভ্যালু ব্যবহার করেন তা থেকে V8 অনুমান করে যে আপনি কোন ধরনের সংখ্যা নিয়ে কাজ করছেন। V8 এই অনুমান করার পরে, এটি দক্ষতার সাথে মান উপস্থাপন করতে ট্যাগিং ব্যবহার করে, কারণ এই ধরনগুলি ডাইনামিকভাবে পরিবর্তিত হতে পারে। তবে, কখনও কখনও এই ধরনের ট্যাগ পরিবর্তন করার জন্য খরচ হয়, তাই ধারাবাহিকভাবে সংখ্যার ধরন ব্যবহার করা সবচেয়ে ভালো এবং সাধারণভাবে যেখানে উপযুক্ত সেখানে ৩১-বিট সাইনড ইন্টিজার ব্যবহার করা সবচেয়ে ভালো।
যেমন:
var i = 42; // this is a 31-bit signed integer
var j = 4.2; // this is a double-precision floating point number```
সুতরাং
- ৩১-বিট সাইনড ইন্টিজার হিসেবে দেখানো যায় এমন সংখ্যাসূচক ভ্যালু ব্যবহার করা পছন্দ করুন।
অ্যারে
বড় এবং স্পার্স অ্যারে ম্যানেজ করার জন্য, ইন্টার্নালি দুই ধরনের অ্যারে স্টোরেজ আছে:
- Fast Elements: কমপ্যাক্ট কী সেটের জন্য লিনিয়ার স্টোরেজ
- ডিকশনারি এলিমেন্ট: অন্যথায় হ্যাশ টেবিল স্টোরেজ
অ্যারে স্টোরেজ এক ধরন থেকে অন্য ধরনে পরিবর্তন না করাই ভালো।
সুতরাং
- অ্যারের জন্য 0 থেকে শুরু করে সংলগ্ন কী ব্যবহার করুন
- সর্বাধিক সাইজের জন্য বড় অ্যারে (যেমন, > ৬৪ হাজার এলিমেন্ট) আগে থেকে বরাদ্দ করবেন না, পরিবর্তে প্রয়োজন অনুযায়ী বাড়ান
- অ্যারেতে এলিমেন্ট মুছবেন না, বিশেষ করে নিউমেরিক অ্যারে
- শুরু না করা বা মুছে ফেলা এলিমেন্ট লোড করবেন না:
for (var b = 0; b < 10; b++) {
a[0] |= b; // Oh no!
}
//vs.
a = new Array();
a[0] = 0;
for (var b = 0; b < 10; b++) {
a[0] |= b; // Much better! 2x faster.
}
এছাড়াও, ডাবলসের অ্যারে দ্রুত কাজ করে - অ্যারের লুকানো ক্লাস এলিমেন্টের ধরন ট্র্যাক করে এবং শুধুমাত্র ডাবলস থাকা অ্যারে আনবক্স করা হয় (যার ফলে লুকানো ক্লাস পরিবর্তন হয়)। তবে, অ্যারের অসতর্ক ম্যানিপুলেশন বক্সিং এবং আনবক্সিংয়ের কারণে অতিরিক্ত কাজ করতে পারে - যেমন:
var a = new Array();
a[0] = 77; // Allocates
a[1] = 88;
a[2] = 0.5; // Allocates, converts
a[3] = true; // Allocates, converts```
এর থেকে কম দক্ষ:
var a = [77, 88, 0.5, true];
কারণ প্রথম উদাহরণে, আলাদা আলাদা অ্যাসাইনমেন্ট একের পর এক করা হয় এবং a[2]-এর অ্যাসাইনমেন্টের ফলে অ্যারেটি আনবক্সড ডাবলসের অ্যারেতে কনভার্ট হয়ে যায়, কিন্তু তারপর a[3]-এর অ্যাসাইনমেন্টের ফলে এটি আবার এমন একটি অ্যারেতে কনভার্ট হয়ে যায় যাতে যেকোনও ভ্যালু (সংখ্যা বা অবজেক্ট) থাকতে পারে। দ্বিতীয় ক্ষেত্রে, কম্পাইলার লিটারেলের সব এলিমেন্টের ধরন জানে এবং হিডেন ক্লাস আগে থেকেই নির্ধারণ করা যায়।
- ছোট নির্দিষ্ট-সাইজের অ্যারের জন্য অ্যারে লিটারাল ব্যবহার করে শুরু করুন
- ছোট অ্যারে (<৬৪k) ব্যবহার করার আগে সঠিক সাইজে প্রি-অ্যালোকেট করুন
- নিউমেরিক অ্যারেতে নন-নিউমেরিক ভ্যালু (অবজেক্ট) সেভ করবেন না
- আপনি যদি লিটারেল ছাড়া শুরু করেন, তাহলে ছোট অ্যারে যাতে আবার কনভার্ট না হয় সেই ব্যাপারে সতর্ক থাকুন।
জাভাস্ক্রিপ্ট কম্পাইলেশন
জাভাস্ক্রিপ্ট একটি অত্যন্ত ডাইনামিক ভাষা এবং এর আসল ইমপ্লিমেন্টেশন ইন্টারপ্রেটার হলেও, আধুনিক জাভাস্ক্রিপ্ট রানটাইম ইঞ্জিন কম্পাইলেশন ব্যবহার করে। V8 (Chrome-এর জাভাস্ক্রিপ্ট)-এ দুটি আলাদা জাস্ট-ইন-টাইম (JIT) কম্পাইলার আছে, আসলে:
- "সম্পূর্ণ" কম্পাইলার, যা যেকোনও জাভাস্ক্রিপ্টের জন্য ভালো কোড তৈরি করতে পারে
- অপ্টিমাইজিং কম্পাইলার, যা বেশিরভাগ জাভাস্ক্রিপ্টের জন্য দুর্দান্ত কোড তৈরি করে, তবে কম্পাইল করতে বেশি সময় নেয়।
সম্পূর্ণ কম্পাইলার
V8-এ, ফুল কম্পাইলার সব কোডে রান করে এবং যত তাড়াতাড়ি সম্ভব কোড এক্সিকিউট করা শুরু করে, দ্রুত ভাল কোড জেনারেট করে, তবে তা খুব ভাল নয়। এই কম্পাইলার কম্পাইলেশনের সময় টাইপ সম্পর্কে প্রায় কিছুই অনুমান করে না - এটি আশা করে যে রানটাইমে ভেরিয়েবলের টাইপ পরিবর্তন হতে পারে এবং হবে। Full কম্পাইলারের মাধ্যমে জেনারেট করা কোড Inline Caches (ICs) ব্যবহার করে প্রোগ্রাম চলাকালীন টাইপ সম্পর্কে জ্ঞান রিফাইন করে, যা ফ্লাইতে দক্ষতা উন্নত করে।
ইনলাইন ক্যাশের লক্ষ্য হল অপারেশনের জন্য ধরন-নির্ভর কোড ক্যাশ করার মাধ্যমে দক্ষতার সাথে ধরন ম্যানেজ করা; কোড রান করার সময়, এটি প্রথমে ধরন সংক্রান্ত অনুমান যাচাই করবে, তারপর অপারেশন শর্টকাট করতে ইনলাইন ক্যাশে ব্যবহার করবে। তবে, এর অর্থ হল একাধিক ধরনের অপারেশন কম পারফর্ম করবে।
সুতরাং
- পলিমরফিক অপারেশনের চেয়ে মনোমরফিক অপারেশন ব্যবহার করাকে বেশি গুরুত্ব দেওয়া হয়
ইনপুটের হিডেন ক্লাস সবসময় একই হলে অপারেশন মনোমরফিক হয় - অন্যথায় সেগুলি পলিমরফিক হয়, যার অর্থ হল অপারেশনের বিভিন্ন কল জুড়ে কিছু আর্গুমেন্ট টাইপ পরিবর্তন করতে পারে। যেমন, এই উদাহরণে দ্বিতীয় add() কল পলিমরফিজম ঘটায়:
function add(x, y) {
return x + y;
}
add(1, 2); // + in add is monomorphic
add("a", "b"); // + in add becomes polymorphic```
অপ্টিমাইজিং কম্পাইলার
সম্পূর্ণ কম্পাইলারের পাশাপাশি, V8 একটি অপ্টিমাইজিং কম্পাইলারের সাথে "হট" ফাংশন (অর্থাৎ, যে ফাংশনগুলি অনেকবার চালানো হয়) পুনরায় কম্পাইল করে। এই কম্পাইলার কম্পাইল করা কোডকে আরও দ্রুত করতে টাইপ ফিডব্যাক ব্যবহার করে - আসলে, এটি আমরা এইমাত্র যে IC-এর কথা বলেছি সেখান থেকে নেওয়া টাইপ ব্যবহার করে!
অপ্টিমাইজ করা কম্পাইলারে, অপারেশনগুলি অনুমানমূলকভাবে ইনলাইন করা হয় (যেখানে তাদের কল করা হয় সেখানে সরাসরি রাখা হয়)। এটি এক্সিকিউশন দ্রুত করে (মেমরি ফুটপ্রিন্টের খরচে), তবে অন্যান্য অপ্টিমাইজেশনও চালু করে। মনোমর্ফিক ফাংশন এবং কনস্ট্রাক্টর সম্পূর্ণভাবে ইনলাইন করা যেতে পারে (এটি আরেকটি কারণ যার জন্য V8-এ মনোমরফিজম একটি ভালো ধারণা)।
V8 ইঞ্জিনের স্ট্যান্ডঅ্যালোন "d8" ভার্সন ব্যবহার করে আপনি অপ্টিমাইজ করা ডেটা লগ করতে পারেন:
d8 --trace-opt primes.js
(এটি stdout-এ অপ্টিমাইজ করা ফাংশনের নাম লগ করে।)
তবে, সব ফাংশন অপ্টিমাইজ করা যায় না - কিছু ফিচার অপ্টিমাইজ করা কম্পাইলারকে নির্দিষ্ট ফাংশনে রান করা থেকে আটকায় ("বেল-আউট")। বিশেষ করে, অপ্টিমাইজ করা কম্পাইলার বর্তমানে try {} catch {} ব্লক সহ ফাংশন থেকে বেরিয়ে আসে!
সুতরাং
- আপনার try {} catch {} ব্লক থাকলে, পারফর্ম্যান্স-সেনসিটিভ কোড নেস্টেড ফাংশনে রাখুন: ```js function perf_sensitive() { // Do performance-sensitive work here }
try { perf_sensitive() } catch (e) { // Handle exceptions here } ```
এই নির্দেশিকা ভবিষ্যতে পরিবর্তিত হতে পারে, কারণ আমরা অপ্টিমাইজ করা কম্পাইলারে try/catch ব্লক চালু করি। উপরে দেখানো d8-এর সাথে "--trace-opt" বিকল্প ব্যবহার করে আপনি পরীক্ষা করতে পারেন যে অপ্টিমাইজ করা কম্পাইলার কীভাবে ফাংশন থেকে বেরিয়ে আসছে, যা আপনাকে কোন ফাংশন থেকে বেরিয়ে এসেছে সে সম্পর্কে আরও তথ্য দেয়:
d8 --trace-opt primes.js
ডি-অপ্টিমাইজেশন
সবশেষে, এই কম্পাইলারের মাধ্যমে করা অপ্টিমাইজেশন অনুমানভিত্তিক - কখনও কখনও এটি কাজ করে না এবং আমরা পিছিয়ে আসি। "ডিঅপ্টিমাইজেশন" প্রসেস অপ্টিমাইজ করা কোড বাতিল করে দেয় এবং "সম্পূর্ণ" কম্পাইলার কোডের সঠিক জায়গায় এক্সিকিউশন আবার শুরু করে। পরে আবার রি-অপ্টিমাইজেশন ট্রিগার করা হতে পারে, তবে অল্প সময়ের জন্য এক্সিকিউশন স্লো হয়ে যায়। বিশেষ করে, ফাংশন অপ্টিমাইজ করার পরে ভেরিয়েবলের লুকানো ক্লাসে পরিবর্তন করলে এই ডিঅপ্টিমাইজেশন ঘটবে।
সুতরাং
- ফাংশন অপ্টিমাইজ করার পরে সেগুলিতে লুকানো ক্লাস পরিবর্তন এড়িয়ে চলুন
অন্যান্য অপ্টিমাইজেশনের মতো, আপনি লগিং ফ্ল্যাগ সহ V8-কে ডিঅপ্টিমাইজ করতে হয়েছে এমন ফাংশনের লগ পেতে পারেন:
d8 --trace-deopt primes.js
অন্যান্য V8 টুল
এছাড়াও, আপনি স্টার্টআপে Chrome-এ V8 ট্রেসিং বিকল্প পাস করতে পারেন:
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --js-flags="--trace-opt --trace-deopt"```
ডেভেলপার টুল প্রোফাইলিং ব্যবহার করার পাশাপাশি, আপনি প্রোফাইলিং করার জন্য d8 ব্যবহার করতে পারেন:
% out/ia32.release/d8 primes.js --prof
এটি বিল্ট-ইন স্যাম্পলিং প্রোফাইলার ব্যবহার করে, যা প্রতি মিলিসেকেন্ডে একটি স্যাম্পেল নেয় এবং v8.log লেখে।
সংক্ষেপে
পারফর্ম্যান্ট জাভাস্ক্রিপ্ট তৈরি করার জন্য V8 ইঞ্জিন কীভাবে আপনার কোডের সাথে কাজ করে তা শনাক্ত করা এবং বোঝা গুরুত্বপূর্ণ। আরও একবার, প্রাথমিক পরামর্শ হল:
- কোনও সমস্যা হওয়ার (বা লক্ষ্য করার) আগে থেকেই প্রস্তুত থাকুন
- তারপর, আপনার সমস্যার মূল কারণ শনাক্ত করুন ও বুঝুন
- অবশেষে, গুরুত্বপূর্ণ সমস্যার সমাধান করুন
এর অর্থ হল, প্রথমে PageSpeed-এর মতো অন্যান্য টুল ব্যবহার করে আপনার জাভাস্ক্রিপ্টে সমস্যা আছে কিনা তা নিশ্চিত করতে হবে; মেট্রিক সংগ্রহ করার আগে সম্ভবত পিওর জাভাস্ক্রিপ্টে (DOM ছাড়া) কমিয়ে আনতে হবে এবং তারপরে সেই মেট্রিকগুলি ব্যবহার করে বাধাগুলি খুঁজে বের করতে হবে এবং গুরুত্বপূর্ণগুলি বাদ দিতে হবে। আশা করি ড্যানিয়েলের বক্তব্য (এবং এই নিবন্ধ) থেকে আপনি আরও ভালোভাবে বুঝতে পারবেন যে V8 কীভাবে জাভাস্ক্রিপ্ট রান করে - তবে আপনার নিজস্ব অ্যালগরিদম অপ্টিমাইজ করার ব্যাপারেও ফোকাস করতে ভুলবেন না!