javascript
JavaScript 引擎和 Just-in-Time 编译概念,Hot Function 的简单介绍
原文:JavaScript engines and Just-In-Time compilation: A beginner’s exploration, part 1
JavaScript 引擎本身也是一種軟件,它將您華麗的 JavaScript 代碼行轉(zhuǎn)換為我們的機(jī)器可執(zhí)行的二進(jìn)制代碼。
所有主要瀏覽器都開發(fā)了自己的 JavaScript 引擎。 Chrome 有 V8,Firefox 運(yùn)行 SpiderMonkey(第一個(gè) JavaScript 引擎的進(jìn)化產(chǎn)品,由 Brendan Eich 在 90 年代為 Netscape Navigator 開發(fā)),Microsoft Edge 有 Chakra,Safari 有 Nitro。 Node.js 建立在 Chrome 的 V8 引擎之上。 物聯(lián)網(wǎng)設(shè)備也可以有一個(gè) JavaScript 引擎。
每個(gè) JavaScript 引擎都負(fù)責(zé)使用 Ecma International 的 TC39 制定的 ECMAScript 規(guī)則和標(biāo)準(zhǔn)。
Why modern JavaScript engines do Just-In-Time compilation
JavaScript 是一門動態(tài)類型語言。
let x = 8 let y = "Henlo fren"這意味著無論何時(shí)你在 JavaScript 中聲明一個(gè)變量,你都不必明確說明變量 x 存儲的信息類型。 JavaScript 引擎在執(zhí)行源代碼時(shí)檢查類型。
在靜態(tài)類型語言(如 C++)中聲明變量時(shí),您必須顯式指定變量值的類型。
int x = 8 string y = "Henlo fren"有了如此嚴(yán)格的規(guī)則,靜態(tài)類型語言可以具有更高的學(xué)習(xí)曲線。在嘗試編寫一個(gè)簡單的程序之前,您必須更多地了解它的規(guī)則和類型。
然而,從編譯器的角度來看,靜態(tài)類型語言允許更快的性能。預(yù)先,當(dāng)編譯器開始將代碼轉(zhuǎn)換為可執(zhí)行的機(jī)器代碼二進(jìn)制時(shí),該語言為編譯器提供了大量有關(guān)源代碼的信息。
另一方面,像 JavaScript 這樣的動態(tài)類型語言很少向編譯器提供有關(guān)其類型的信息。這為編譯器在生成機(jī)器代碼之前創(chuàng)建了另一層工作,使其執(zhí)行速度比靜態(tài)編寫語言的編譯慢。
But fear not, this is where Just-In-Time compilation comes in!
最初開發(fā) JavaScript 時(shí),它旨在編寫少量用于增強(qiáng)網(wǎng)頁的腳本。隨著開發(fā)人員開始構(gòu)建和使用更多 JavaScript 框架和庫,以及發(fā)出 AJAX 請求,對更好、更快性能的需求不斷增長。
當(dāng) Chrome 于 2008 年推出時(shí),谷歌還首次發(fā)布了其 V8 引擎,這是現(xiàn)代 JavaScript 引擎中的第一個(gè)。 V8 的主要特性之一是即時(shí)編譯 - Just-In-Time compilation。
在 Ahead-of-Time 編譯中,編譯過程必須在系統(tǒng)運(yùn)行可執(zhí)行機(jī)器代碼之前完成。有了 Just-In-Time compilation 這一新特性,V8引擎會根據(jù)需要編譯源代碼,在執(zhí)行編譯過程生成的機(jī)器碼時(shí)收集類型信息,然后根據(jù)執(zhí)行過程收集的信息重新編譯源代碼。兩個(gè)進(jìn)程之間的來回加快了執(zhí)行過程的性能。
為了讓 JavaScript 在動態(tài)類型的情況下仍能以最快的速度運(yùn)行,JavaScript 引擎有一些巧妙的技巧。
像大多數(shù)現(xiàn)代 JavaScript 引擎一樣,V8 有兩個(gè)編譯器:基線 (baseline)編譯器和優(yōu)化編譯器。
當(dāng) V8 編譯你的 JavaScript 代碼時(shí),它的解析器會生成一種叫做抽象語法樹的東西。Ignition,V8 的基線編譯器或解釋器,從這個(gè)語法樹生成字節(jié)碼。 Ignition 忠實(shí)于它的即時(shí)編譯特性,它編譯 JavaScript 代碼,運(yùn)行它,編譯它,運(yùn)行它,來回,一遍又一遍。
在運(yùn)行時(shí),字節(jié)碼被分析,引擎識別可以重新編譯以獲得最佳性能的部分(“熱函數(shù)”),將該代碼發(fā)送到 TurboFan,它是 V8 的優(yōu)化編譯器。正是因?yàn)榧磿r(shí)編譯,引擎才能夠因?yàn)榧磿r(shí)編譯而識別這些所謂的“熱功能”。
The + operator and V8 optimization
在她的精彩演講 JavaScript 引擎中,V8 工程師 Franziska Hinkelmann 使用 + 運(yùn)算符來解釋 V8 的優(yōu)化是如何工作的。
乍一看,加法運(yùn)算符可能看起來很簡單,任何編譯器都可以編譯和執(zhí)行。 但是,如果您查看 Ecma 規(guī)范,在程序?qū)嶋H添加任何內(nèi)容之前,引擎實(shí)際上需要執(zhí)行很多步驟:
這些步驟中的每一步都在調(diào)用其他函數(shù),而這些函數(shù)又可能調(diào)用其他函數(shù),依此類推。所有引擎都必須遵循這些 Ecma 規(guī)范,因此 JavaScript 不僅僅是無法無天的。
因此,當(dāng)您的程序有一個(gè)將兩個(gè)整數(shù)相加的函數(shù)時(shí),當(dāng)您第一次調(diào)用該函數(shù)時(shí),JavaScript 引擎會費(fèi)力地完成這些步驟中的每一步,最終將您的兩個(gè)整數(shù)相加。當(dāng)它通過 JIT 過程(編譯、運(yùn)行、編譯、運(yùn)行、編譯等)時(shí),它意識到你的函數(shù)很熱,很熱,很熱,因?yàn)槟阋恢痹谡{(diào)用它。從引擎在運(yùn)行時(shí)收集的信息,它也意識到這個(gè)特定函數(shù)使用的數(shù)據(jù)類型只是整數(shù)。有了這些信息,V8 將您的代碼發(fā)送到 TurboFan,它的優(yōu)化器編譯器,它為您的函數(shù)生成更好的機(jī)器代碼。下次您再次調(diào)用該函數(shù)時(shí),它會跳過冗長的 Ecma 步驟,您的函數(shù)將運(yùn)行得更快。
但是當(dāng)您決定在調(diào)用該函數(shù)時(shí)連接一些字符串而不是添加兩個(gè)整數(shù)時(shí)會發(fā)生什么? V8 將該函數(shù)拋出到去優(yōu)化器,將其發(fā)送回 Ignition,然后 Igntion 再次執(zhí)行那些 Ecma 指定的步驟來運(yùn)行該函數(shù)。
更多Jerry的原創(chuàng)文章,盡在:“汪子熙”:
總結(jié)
以上是生活随笔為你收集整理的JavaScript 引擎和 Just-in-Time 编译概念,Hot Function 的简单介绍的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 斐讯n1进入u盘启动
- 下一篇: SAP Spartacus 会使用 Se