موتور V8 چطور جاوااسکریپت رو اجرا میکنه؟
خلاصهٔ کاملتر
موتور V8 که داخل Node.js نشسته، جاوااسکریپت رو تفسیر نمیکنه؛ کامپایل میکنه. این تفاوت مهمیه. V8 از یه پایپلاین چهارمرحلهای استفاده میکنه که هدفش اینه کدِ داغ رو به سریعترین ماشینکدِ ممکن تبدیل کنه.
مرحله اول Ignition هست؛ یه مفسر که سورس کد رو به بایتکد تبدیل میکنه و همزمان اطلاعاتی درباره نوع دادهها جمع میکنه (به این میگن Type Feedback). وقتی یه تابع بهاندازه کافی گرم شد، Sparkplug بدون هیچ بهینهسازی خاصی بایتکد رو به ماشینکد تبدیل میکنه. بعد Maglev وارد میشه که یه کامپایلر میانهرو با بهینهسازیهای معقوله. و در نهایت، داغترین مسیرها میرن پیش TurboFan که با فرضهای تهاجمی درباره نوع دادهها، ماشینکد بسیار سریع تولید میکنه.
اگر فرضهای TurboFan نقض بشن، اتفاقی میافته که بهش Deoptimization میگن؛ یعنی کد بهینهشده دور انداخته میشه و اجرا به یه مرحله پایینتر برمیگرده. این فرایند صحت کد رو تضمین میکنه ولی روی پرفورمنس ضربه سنگینی میزنه، خصوصاً اگر مدام تکرار بشه.
مهمترین مفهوم داخلی V8 برای توسعهدهندهها Hidden Class (یا Shape) هست. V8 بهجای اینکه آبجکتهای جاوااسکریپت رو مثل یه دیکشنری ساده ببینه، برای هر «شکل» آبجکت یه کلاس مخفی میسازه. وقتی دو آبجکت همون propertyها رو با همون ترتیب داشته باشن، یه Hidden Class مشترک دارن و V8 میتونه دسترسی به propertyهاشون رو به یه دستورالعمل مستقیم حافظه تبدیل کنه. ولی اگه propertyها به ترتیبهای مختلف اضافه بشن، Hidden Classهای جداگانهای ساخته میشه و بهینهسازی از بین میره.
یه مثال واقعی از این مشکل؛ کدی که یه property رو بهصورت شرطی اضافه میکرد:
function createConfig(base, userOverrides, requestParams) {
let config = { ...base };
for (const key in userOverrides) {
config[key] = userOverrides[key];
}
if (requestParams.useNewFeature) {
config.optionalFeature = true; // این خط Hidden Class رو دو شاخه میکنه
}
return config;
}همین یه خط شرطی باعث شد لیتنسی اندپوینت از ۲ میلیثانیه به ۲۰۰ میلیثانیه برسه. راهحل، پیشمقداردهی همهی propertyها از ابتدا بود:
let config = {
...base,
settingA: null,
settingB: null,
optionalFeature: false, // همیشه وجود داره، حتی با مقدار پیشفرض
};با این کار همه آبجکتهای config یه Hidden Class مشترک داشتن و TurboFan تونست کد بهینهای تولید کنه که روی اونها ثابت بمونه. لیتنسی دوباره به ۲ میلیثانیه رسید.
مکانیزمی که Hidden Classها رو به سرعت واقعی تبدیل میکنه Inline Cache (IC) هست. هر بار که V8 یه property access میبینه، Hidden Class آبجکت رو چک میکنه. اگه همیشه یه Hidden Class باشه (حالت Monomorphic)، V8 میتونه کش کنه و مستقیماً به آدرس حافظه بزنه. اما اگه در یه call site چند شکل مختلف ببینه (حالت Polymorphic یا Megamorphic)، این بهینهسازی از بین میره.
نکات کلیدی:
- V8 از یه پایپلاین ۴ مرحلهای (Ignition → Sparkplug → Maglev → TurboFan) برای کامپایل جاوااسکریپت استفاده میکنه
- Deoptimization یعنی کد بهینه دور انداخته میشه؛ اگه مداوم باشه، پرفورمنس رو خراب میکنه
- Hidden Classها شکل آبجکتها رو توصیف میکنن؛ ترتیب اضافه کردن propertyها مهمه
- اضافه کردن propertyهای شرطی Hidden Classهای متفاوت میسازه و بهینهسازی رو خراب میکنه
- برای آبجکتهای پرکاربرد، همه propertyها رو از اول با مقدار پیشفرض مشخص کن
- Inline Cacheها وقتی با یه شکل ثابت روبرو بشن (monomorphic) بهترین عملکرد رو دارن




