天天看點

JavaScript 引擎和 Just-in-Time 編譯概念,Hot Function 的簡單介紹

JavaScript 引擎和 Just-in-Time 編譯概念,Hot Function 的簡單介紹

有了如此嚴格的規則,靜态類型語言可以具有更高的學習曲線。在嘗試編寫一個簡單的程式之前,您必須更多地了解它的規則和類型。

然而,從編譯器的角度來看,靜态類型語言允許更快的性能。預先,當編譯器開始将代碼轉換為可執行的機器代碼二進制時,該語言為編譯器提供了大量有關源代碼的資訊。

另一方面,像 JavaScript 這樣的動态類型語言很少向編譯器提供有關其類型的資訊。這為編譯器在生成機器代碼之前建立了另一層工作,使其執行速度比靜态編寫語言的編譯慢。

But fear not, this is where Just-In-Time compilation comes in!

最初開發 JavaScript 時,它旨在編寫少量用于增強網頁的腳本。随着開發人員開始建構和使用更多 JavaScript 架構和庫,以及發出 AJAX 請求,對更好、更快性能的需求不斷增長。

當 Chrome 于 2008 年推出時,谷歌還首次釋出了其 V8 引擎,這是現代 JavaScript 引擎中的第一個。 V8 的主要特性之一是即時編譯 - Just-In-Time compilation。

在 Ahead-of-Time 編譯中,編譯過程必須在系統運作可執行機器代碼之前完成。有了 Just-In-Time compilation 這一新特性,V8引擎會根據需要編譯源代碼,在執行編譯過程生成的機器碼時收集類型資訊,然後根據執行過程收集的資訊重新編譯源代碼。兩個程序之間的來回加快了執行過程的性能。

為了讓 JavaScript 在動态類型的情況下仍能以最快的速度運作,JavaScript 引擎有一些巧妙的技巧。

像大多數現代 JavaScript 引擎一樣,V8 有兩個編譯器:基線 (baseline)編譯器和優化編譯器。

當 V8 編譯你的 JavaScript 代碼時,它的解析器會生成一種叫做抽象文法樹的東西。Ignition,V8 的基線編譯器或解釋器,從這個文法樹生成位元組碼。 Ignition 忠實于它的即時編譯特性,它編譯 JavaScript 代碼,運作它,編譯它,運作它,來回,一遍又一遍。

在運作時,位元組碼被分析,引擎識别可以重新編譯以獲得最佳性能的部分(“熱函數”),将該代碼發送到 TurboFan,它是 V8 的優化編譯器。正是因為即時編譯,引擎才能夠因為即時編譯而識别這些所謂的“熱功能”。

JavaScript 引擎和 Just-in-Time 編譯概念,Hot Function 的簡單介紹

The + operator and V8 optimization

在她的精彩演講 JavaScript 引擎中,V8 工程師 Franziska Hinkelmann 使用 + 運算符來解釋 V8 的優化是如何工作的。

乍一看,加法運算符可能看起來很簡單,任何編譯器都可以編譯和執行。 但是,如果您檢視 Ecma 規範,在程式實際添加任何内容之前,引擎實際上需要執行很多步驟:

JavaScript 引擎和 Just-in-Time 編譯概念,Hot Function 的簡單介紹

這些步驟中的每一步都在調用其他函數,而這些函數又可能調用其他函數,依此類推。所有引擎都必須遵循這些 Ecma 規範,是以 JavaScript 不僅僅是無法無天的。

是以,當您的程式有一個将兩個整數相加的函數時,當您第一次調用該函數時,JavaScript 引擎會費力地完成這些步驟中的每一步,最終将您的兩個整數相加。當它通過 JIT 過程(編譯、運作、編譯、運作、編譯等)時,它意識到你的函數很熱,很熱,很熱,因為你一直在調用它。從引擎在運作時收集的資訊,它也意識到這個特定函數使用的資料類型隻是整數。有了這些資訊,V8 将您的代碼發送到 TurboFan,它的優化器編譯器,它為您的函數生成更好的機器代碼。下次您再次調用該函數時,它會跳過冗長的 Ecma 步驟,您的函數将運作得更快。

但是當您決定在調用該函數時連接配接一些字元串而不是添加兩個整數時會發生什麼? V8 将該函數抛出到去優化器,将其發送回 Ignition,然後 Igntion 再次執行那些 Ecma 指定的步驟來運作該函數。

繼續閱讀