在上一篇文章中我們講了如何使用 GN 編譯 V8 源碼榴啸,文章最后編譯完成的可執(zhí)行文件并不是 V8渤滞,而是 D8墓怀。這篇我們講一下如何使用 D8 調(diào)試 javascript 代碼弹囚。
如果沒有 d8揉稚,可以使用 node 代替秒啦。
新建文件 add-of-ints.js,輸入以下內(nèi)容:
function add(obj) {
return obj.prop + obj.prop;
}
const length = 1000 * 1000;
const o = { prop: 1 };
for (let i = 0; i < length; i++) {
add(o);
}
運(yùn)行:
d8 --trace-opt-verbose add-of-ints.js
或
node --trace-opt-verbose add-of-ints.js
輸出結(jié)果為:
從輸出結(jié)果我們可以看到 add 函數(shù)被編譯器優(yōu)化了搀玖,并且解釋了優(yōu)化的原因余境。ICs 是 inline caches 的縮寫,內(nèi)聯(lián)緩存是一種很常見的優(yōu)化技術(shù)灌诅,這段簡短的代碼被 V8 引擎優(yōu)化了兩次芳来,但是原因卻不同。
第一次優(yōu)化的原因是 small function猜拾,add 函數(shù)是小函數(shù)即舌,為了減小函數(shù)調(diào)用的開銷,V8 引擎對 add 做了優(yōu)化挎袜。
第二次的原因是 hot and stable顽聂,我在知乎另一個(gè)問題中曾說過,V8 有兩個(gè)編譯器盯仪,一個(gè)通用編譯器紊搪,負(fù)責(zé)將 javascript 代碼編譯為機(jī)器碼,另一個(gè)是優(yōu)化編譯器全景。從上面的輸出可以看出 V8 使用的優(yōu)化編譯器引擎是 Crankshaft耀石。Crankshaft 負(fù)責(zé)找出經(jīng)常被調(diào)用的代碼,做內(nèi)聯(lián)緩存優(yōu)化爸黄,后面的信息進(jìn)一步說明了這個(gè)情況:ICs with typeinfo: 7/7 (100%), generic ICs: 0/7 (0%)滞伟。
在此再糾正之前的 2 個(gè)問題揭鳞。
一個(gè)是 V8 沒有解釋器,只有編譯器诗良,代碼是直接編譯成機(jī)器嗎執(zhí)行的汹桦,這是之前的 V8,而網(wǎng)絡(luò)上關(guān)于 V8 的文章也大多比較老舊鉴裹。這幾天為了閱讀 V8 源碼查看了網(wǎng)上很多關(guān)于 V8 的論文和文章舞骆,發(fā)現(xiàn) V8 已經(jīng)引進(jìn)了解釋器。因?yàn)?V8 不僅僅可以優(yōu)化代碼径荔,還可以去優(yōu)化(deopt)督禽,引入解釋器可以省去一些代碼的重編譯時(shí)間,另一個(gè)原因是解釋器不僅僅可以解釋 javascript 代碼总处,還可以解釋 asm 或者其他二進(jìn)制中間碼狈惫。
另一個(gè)錯(cuò)誤就是關(guān)于 V8 優(yōu)化的,之前寫過 JavaScript 函數(shù)式編程存在性能問題么鹦马? 中道:
永遠(yuǎn)不可能被優(yōu)化的有:
- Functions that contain a debugger statement
- Functions that call literally eval()
- Functions that contain a with statement
這個(gè)也是之前的文章胧谈,是以 Crankshaft 引擎為標(biāo)準(zhǔn)得出的結(jié)論。而 V8 已經(jīng)開發(fā)了新的優(yōu)化引擎——TurboFan荸频。
我們再創(chuàng)建另一個(gè)文件 add-of-mixed.js菱肖,輸入:
// flag: --trace-opt-verbose
function add(obj) {
return obj.prop + obj.prop;
}
var length = 1000 * 1000;
var objs = new Array(length);
var i = 0;
for (i = 0; i < length; i++) {
objs[i] = Math.random();
}
var a = { prop: 'a' };
var b = { prop: 1 };
for (i = 0; i < length; i++) {
add(objs[i] > 0.5 ? a : b);
}
運(yùn)行:
d8 --trace-opt-verbose add-of-mixed.js
或
node --trace-opt-verbose add-of-mixed.js
輸出結(jié)果為:
可以看到這段代碼能不能做內(nèi)聯(lián)緩存優(yōu)化全看 RP(人品) 了。
我們再使用 --trace-opt --trace-deopt
參數(shù)看看 V8 引擎如何去優(yōu)化旭从。
新建文件 add-of-mixed-dep.js稳强,輸入:
// flags: --trace-opt --trace-deopt
function add(obj) {
return obj.prop + obj.prop;
}
var length = 10000;
var i = 0;
var a = { prop: 'a' };
var b = { prop: 1 };
for (i = 0; i < length; i++) {
add(i !== 8000 ? a : b);
}
運(yùn)行:
d8 --trace-opt --trace-deopt add-of-mixed-dep.js
或
node --trace-opt --trace-deopt add-of-mixed-dep.js
結(jié)果為:
V8 引擎內(nèi)部使用 Hidden Classes 來表示 Object,關(guān)于 Hidden Classes 的文章已經(jīng)很多了和悦,我就不累述了退疫。
運(yùn)行 d8 --help
可以查看所有的 d8 命令行參數(shù)。如果使用 node鸽素,直接運(yùn)行 node --help
輸出的是 node 的命令行參數(shù)褒繁,如果想查看 V8 的,需要使用 node --v8-options
馍忽。
后面章節(jié)會(huì)介紹 V8 的 GC(命令行參數(shù) --trace-gc
)以及最有意思的 --allow-natives-syntax
棒坏。
推薦閱讀一下 V8 的 bailout-reason.h 源碼,這是一個(gè) C++ 的頭文件舵匾,里面幾乎沒有任何代碼邏輯,定義了所有 javascript 代碼不能被 V8 引擎優(yōu)化的原因谁不,比如:
"Array index constant value too big"
"eval"
"ForOfStatement"
"Too many parameters"
"WithStatement"
……
后面章節(jié)介紹的 --allow-natives-syntax
相關(guān) C++ 頭文件是 runtime.h坐梯,通過 --allow-natives-syntax
參數(shù)可以在 javascript 中使用 V8 的運(yùn)行時(shí)函數(shù)。我們在之前的文章中已經(jīng)使用過了刹帕,例如 HasFastProperties
吵血。
參考文章: