在上一篇文章中咱們講了如何使用 GN 編譯 V8 源碼,文章最後編譯完成的可執行文件並非 V8,而是 D8。這篇咱們講一下如何使用 D8 調試 javascript 代碼。javascript
若是沒有 d8,可使用 node 代替。html
新建文件 add-of-ints.js,輸入如下內容:html5
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);
}複製代碼
運行:java
d8 --trace-opt-verbose add-of-ints.js
或
node --trace-opt-verbose add-of-ints.js複製代碼
輸出結果爲:node
從輸出結果咱們能夠看到 add 函數被編譯器優化了,而且解釋了優化的緣由。ICs 是 inline caches 的縮寫,內聯緩存是一種很常見的優化技術,這段簡短的代碼被 V8 引擎優化了兩次,可是緣由卻不一樣。git
第一次優化的緣由是 small function,add 函數是小函數,爲了減少函數調用的開銷,V8 引擎對 add 作了優化。github
第二次的緣由是 hot and stable,我在知乎另外一個問題中曾說過,V8 有兩個編譯器,一個通用編譯器,負責將 javascript 代碼編譯爲機器碼,另外一個是優化編譯器。從上面的輸出能夠看出 V8 使用的優化編譯器引擎是 Crankshaft。Crankshaft 負責找出常常被調用的代碼,作內聯緩存優化,後面的信息進一步說明了這個狀況:ICs with typeinfo: 7/7 (100%), generic ICs: 0/7 (0%)。編程
在此再糾正以前的 2 個問題。緩存
一個是 V8 沒有解釋器,只有編譯器,代碼是直接編譯成機器嗎執行的,這是以前的 V8,而網絡上關於 V8 的文章也大多比較老舊。這幾天爲了閱讀 V8 源碼查看了網上不少關於 V8 的論文和文章,發現 V8 已經引進了解釋器。由於 V8 不單單能夠優化代碼,還能夠去優化(deopt),引入解釋器能夠省去一些代碼的重編譯時間,另外一個緣由是解釋器不單單能夠解釋 javascript 代碼,還能夠解釋 asm 或者其餘二進制中間碼。網絡
另外一個錯誤就是關於 V8 優化的,以前寫過 JavaScript 函數式編程存在性能問題麼? 中道:
永遠不可能被優化的有:
- Functions that contain a debugger statement
- Functions that call literally eval()
- Functions that contain a with statement
這個也是以前的文章,是以 Crankshaft 引擎爲標準得出的結論。而 V8 已經開發了新的優化引擎——TurboFan。
咱們再建立另外一個文件 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);
}複製代碼
運行:
d8 --trace-opt-verbose add-of-mixed.js
或
node --trace-opt-verbose add-of-mixed.js複製代碼
輸出結果爲:
能夠看到這段代碼能不能作內聯緩存優化全看 RP(人品) 了。
咱們再使用 --trace-opt --trace-deopt
參數看看 V8 引擎如何去優化。
新建文件 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);
}複製代碼
運行:
d8 --trace-opt --trace-deopt add-of-mixed-dep.js
或
node --trace-opt --trace-deopt add-of-mixed-dep.js複製代碼
結果爲:
V8 引擎內部使用 Hidden Classes 來表示 Object,關於 Hidden Classes 的文章已經不少了,我就不累述了。
運行 d8 --help
能夠查看全部的 d8 命令行參數。若是使用 node,直接運行 node --help
輸出的是 node 的命令行參數,若是想查看 V8 的,須要使用 node --v8-options
。
後面章節會介紹 V8 的 GC(命令行參數 --trace-gc
)以及最有意思的 --allow-natives-syntax
。
推薦閱讀一下 V8 的 bailout-reason.h 源碼,這是一個 C++ 的頭文件,裏面幾乎沒有任何代碼邏輯,定義了全部 javascript 代碼不能被 V8 引擎優化的緣由,好比:
"Array index constant value too big"
"eval"
"ForOfStatement"
"Too many parameters"
"WithStatement"
……複製代碼
後面章節介紹的 --allow-natives-syntax
相關 C++ 頭文件是 runtime.h,經過 --allow-natives-syntax
參數能夠在 javascript 中使用 V8 的運行時函數。咱們在以前的文章中已經使用過了,例如 HasFastProperties
。
參考文章: