探祕 Node.js 10 新功能的背後:V8 release 6.6

每六週,咱們都會建立一個 V8 的新分支,做爲咱們發佈流程的一部分。每一個版本都是在 Chrome Beta 里程碑以前從 V8 的 Git master 分支出來的。今天(2018-03-27),咱們很高興地宣佈,咱們發佈了一個新的分支:V8 version 6.6,在幾個星期內,咱們會發布 Chrome 66 Stable 版,在此以前它依然處於測試階段。V8 v6.6 提供了面向開發人員的一些很酷的特性。本文提供了預期發佈的一些亮點預覽。javascript

JavaScript 語言新特性

Function.prototype.toString() 如今返回源代碼的全部內容,包括空格和註釋。如下是一箇舊行爲和新行爲的對比例子:html

// 注意 `function` 關鍵詞以前的註釋以及空格
function /* a comment */ foo () {}

// 以前版本:
foo.toString();
// → 'function foo() {}'
//             ^ no comment
//                ^ no space

// 新版本:
foo.toString();
// → 'function /* comment */ foo () {}'

行分隔符(U+2028)和段落分隔符(U+2029)如今容許出如今字符串文字中,matching JSON。之前,這些符號被視爲字符串中的行結束符,所以使用它們會致使 SyntaxError 異常。java

在異常捕獲的 catch 子句中能夠不加參數: catch 的參數能夠省略了 optional-catch-binding。若是您不須要在異常代碼中處理 exception 對象,這很是有用。git

try {
  doSomethingThatMightThrow();
} catch { // → Look mom, no binding!
  handleException();
}

除了 String.prototype.trim() 外,V8 如今實現了 String.prototype.trimStart()String.prototype.trimEnd()。此功能之前是經過非標準 trimLeft()trimRight() 方法提供的,這些方法仍然是新方法的別名,用於向後兼容。github

const string = '  hello world  ';
string.trimStart();
// → 'hello world  '
string.trimEnd();
// → '  hello world'
string.trim();
// → 'hello world'

Array.prototype.values() 方法返回了數組的迭代接口,就像 ES2015 的 MapSet 同樣:如今咱們可使用 keysvaluesentries 進行遍歷。此更改有可能與現有的 JavaScript 代碼不兼容。若是您發現某個網站有奇怪的行爲或代碼運行中斷了,請嘗試經過 chrome://flags/#enable-array-prototype-values 禁用此功能,並提出問題chrome

緩存執行過的代碼

「冷加載(cold load)」和「溫加載(warm load)」這兩個術語在關於加載性能方面是衆所周知的。在 V8 中,還有熱加載(hot load)的概念。咱們以 Chrome 爲例說明加載的不一樣級別:json

  • 冷加載(cold load):Chrome 首次看到訪問的網頁,而且根本沒有任何數據緩存。
  • 溫加載(warm load):Chrome 會記住網頁已被訪問,而且能夠從緩存中檢索某些資源(例如圖像和腳本源文件)。V8 意識到頁面使用了相同的腳本文件,所以將編譯後的代碼與腳本文件一塊兒緩存在磁盤緩存中。
  • 熱加載(hot load):Chrome 第三次訪問網頁時,從磁盤緩存載入腳本文件時,它還向 V8 提供上次加載期間緩存的代碼。V8 可使用這個緩存代碼來避免必須從頭開始解析和編譯腳本。

在 V8 v6.6 以前,咱們在頂層編譯後當即緩存生成的代碼。V8 只編譯已知在頂層編譯過程當中當即執行的函數,並將其餘函數標記爲延遲編譯。這意味着緩存代碼只包含頂級代碼,而全部其餘函數必須在每次頁面加載時從頭開始進行延遲編譯。從版本 6.6 開始,V8 會緩存頂級代碼執行以後的腳本生成的代碼。在咱們執行腳本時,更多的函數會被編譯而且能夠被包含在緩存中。所以,這些函數不須要在將來頁面加載時編譯,從而將熱加載(hot load)場景中的編譯和解析時間縮短 20-60%。對最終用戶可見的是,提供了一個不太擁擠的主線程,所以會更順暢,並且有更快的加載體驗。api

以後咱們會編寫此主題相關的詳細博客文章。(已經發布並翻譯:V8 6.6 進一步改進緩存性能數組

後臺編譯

一段時間以來,V8 已經可以在後臺線程上解析 JavaScript 代碼。隨着去年發佈的 V8 新的 Ignition 字節碼解釋器,咱們擴展了這個功能,以便在後臺線程上將 JavaScript 源代碼編譯爲字節碼。這使得嵌入 V8 引擎的軟件能夠在主線程中執行更多工做,來執行更多的 JavaScript 腳本。咱們在 Chrome 66 中啓用了此功能,在一般的網站上,主線程編譯時間減小了 5% 到 20%。有關更多詳細信息,請參閱[此功能的最新博客文章(https://v8project.blogspot.co...promise

移除 AST 編碼

去年 Ignition 和 TurboFan 推出後,咱們繼續從簡化編譯流水線(pipeline)中獲益。之前在代碼解析(parsing)後還須要一個名爲 "AST Numbering(AST編碼)" 的階段,用來對生成的抽象語法樹中的節點進行編號,以後編譯器使用此節點時能夠有共同的引用點。

隨着時間的推移,這個後處理過程(post-processing)已經擴展到包含其餘功能:爲生成器和異步函數的暫停點進行編號,收集須要迫切編譯的內部函數,初始化文字字面量或檢測不可優化的代碼模式。

經過新的流水線(pipeline),Ignition 字節碼成爲經常使用的引用點,而且再也不須要編號 - 可是,仍然須要其它的功能,而且仍然保留了 AST 編號。

在 V8 v6.6 中,咱們終於設法移除了其他的功能或將其移動到了其餘地方,這些工做在解析過程當中完成,從而避免了對 AST 的遍歷。這致使實際編譯時間提升了 3-5%。

異步性能改進

咱們爲 Promise 和異步函數取得了一些不錯的性能改進,特別是設法縮小了異步函數和 promise 鏈之間的差距。

此外,異步生成器和異步迭代的性能也獲得顯着提升,在即將發佈的 Node 10 LTS 版中會包含 V8 v6.6。

做爲一個例子,考慮下面的斐波那契序列:

async function* fibonacciSequence() {
  for (let a = 0, b = 1;;) {
    yield a;
    const c = a + b;
    a = b;
    b = c;
  }
}

async function fibonacci(id, n) {
  for await (const value of fibonacciSequence()) {
    if (n-- === 0) return value;
  }
}

咱們已經測量了這種模式在 Babel transpilation 以前和以後的改進:

最後,字節碼改進也提升了這些「可暫停」函數的運行時性能:生成器,異步函數和模塊,並減小了它們的編譯大小。咱們計劃在即將發佈的版本中進一步改進異步函數和異步生成器的性能,敬請關注。

數組性能改進

Array#reduce 對於 holey double arrays 吞吐量性能提升了 10 倍以上(請參閱咱們的博客文章,瞭解 「holey 數組」和 「packed 數組」是什麼)。

不受信任的代碼保護

在 V8 v6.6 中,咱們針對旁路信道漏洞採起了更多緩解措施,以防止信息泄露給不可信的 JavaScript 和 WebAssembly 代碼。

再也不須要 GYP

這是第一個沒有 GYP 文件的 V8 正式版本。若是您的產品須要 GYP 文件,則須要自行將它們複製到您本身的源代碼庫中。

內存分析

Chrome 的 DevTools 如今能夠跟蹤和快照 C++ DOM 對象,並顯示 JavaScript 引用的全部可訪問的 DOM 對象。這個特性是 V8 垃圾收集器的新 C++ 跟蹤機制的好處之一。欲瞭解更多信息,請查看專門的博客文章:Chrome 66 使用 DevTools 跟蹤 JS 對象和 DOM 對象的引用

V8 API

請使用 git log branch-heads/6.5..branch-heads/6.6 include/v8.h 獲取 API 的變動列表。

對於使用 git 的 V8 開發者,能夠經過 git checkout -b 6.6 -t branch-heads/6.6 簽出 V8 v6.6 中的新功能進行試驗。或者,您能夠訂閱 Chrome 的 Beta 頻道,這樣能夠儘快嘗試新功能。

相關文章
相關標籤/搜索