自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

面試寫:說(shuō)說(shuō)執(zhí)行 JavaScript 的 V8 引擎做了什么?

開(kāi)發(fā) 前端
V8 引擎是由 Google 用 C++ 開(kāi)源的 JavaScript 與 WebAssembly 引擎,目前像是 Chrome 和 Node.js 都是使用 V8 在執(zhí)行 JavaScript。除了 V8 以外還有 SpiderMonkey(最早的 JavaScript 引擎,目前是 Firefox 瀏覽器在使用)與 JavaScriptCore(Safari 瀏覽器使用)等其他 JavaScr

Hi!大家好,我想點(diǎn)進(jìn)來(lái)的大家應(yīng)該都聽(tīng)過(guò),也在瀏覽器或 Node.js 上執(zhí)行過(guò) JavaScript,但你們有想過(guò) JavaScript 是如何執(zhí)行的嗎?這背后的功臣就是 JavaScript 引擎,而標(biāo)題提到的 V8 引擎 也是其中之一喲!

V8 引擎是由 Google 用 C++ 開(kāi)源的 JavaScript 與 WebAssembly 引擎,目前像是 Chrome 和 Node.js 都是使用 V8 在執(zhí)行 JavaScript。除了 V8 以外還有 SpiderMonkey(最早的 JavaScript 引擎,目前是 Firefox 瀏覽器在使用)與 JavaScriptCore(Safari 瀏覽器使用)等其他 JavaScript 引擎。

好的,那麼 V8 引擎到底是如何執(zhí)行 JavaScript 的呢?

V8 引擎執(zhí)行流程

Scanner

V8 引擎取得 JavaScript 源代碼后的第一步,就是讓 Parser 使用 Scanner? 提供的 Tokens(Tokens 裡有 JavaScript 內(nèi)的語(yǔ)法關(guān)鍵字,像是 function、async、if 等),將 JavaScript 的原始碼解析成** abstract syntax tree**,就是大家常在相關(guān)文章中看到的 AST(抽象語(yǔ)法樹)。

如果好奇 AST 長(zhǎng)什麼樣子的話,可以使用 acron 這個(gè) JavaScript Parser,或是 這個(gè)網(wǎng)站 生成 AST 參考看看。以下是使用 acron 的代碼:

const { Parser } = require('acorn')

const javascriptCode = `
let name;
name = 'Clark';
`;

const ast = Parser.parse(javascriptCode, { ecmaVersion: 2020 });
console.log(JSON.stringify(ast));

下方是解析 let name; name = 'Clark'; 所得到的 AST:

{
"type": "Program",
"start": 0,
"end": 31,
"body": [
{
"type": "VariableDeclaration",
"start": 3,
"end": 12,
"declarations": [
{
"type": "VariableDeclarator",
"start": 7,
"end": 11,
"id": {
"type": "Identifier",
"start": 7,
"end": 11,
"name": "name"
},
"init": null
}
],
"kind": "let"
},
{
"type": "ExpressionStatement",
"start": 15,
"end": 30,
"expression": {
"type": "AssignmentExpression",
"start": 15,
"end": 29,
"operator": "=",
"left": {
"type": "Identifier",
"start": 15,
"end": 19,
"name": "name"
},
"right": {
"type": "Literal",
"start": 22,
"end": 29,
"value": "Clark",
"raw": "'Clark'"
}
}
}
],
"sourceType": "script"
}

如果再進(jìn)一步,將上方的 AST 轉(zhuǎn)化成圖表,會(huì)長(zhǎng)這樣:

圖片

AST 可以從上到下,由左而右去理解它在執(zhí)行的步驟:

  • 走 VariableDeclaration 建立名字為name 的變量。
  • 走ExpressionStatement 到表達(dá)式。
  • 走AssignmentExpression? 遇到=?,且左邊為name?,右邊為字串Clark。

產(chǎn)生 AST 后,就完成了 V8 引擎的第一個(gè)步驟。

JIT(Just-In-Time)

JIT 的中文名稱是即時(shí)編譯,這也是 V8 引擎所採(cǎi)用在執(zhí)行時(shí)編譯 JavaScript 的方式。

將代碼轉(zhuǎn)變?yōu)榭蓤?zhí)行的語(yǔ)言有幾種方法,第一種是編譯語(yǔ)言,像是 C/C++ 在寫完代碼的時(shí)候,會(huì)先經(jīng)過(guò)編譯器將代碼變成機(jī)器碼才能執(zhí)行。第二種就像 JavaScript,會(huì)在執(zhí)行的時(shí)候?qū)⒋a解釋成機(jī)器懂的語(yǔ)言,一邊解釋邊執(zhí)行的這種,稱作直譯語(yǔ)言。

編譯語(yǔ)言的好處是可以在執(zhí)行前的編譯階段,審視所有的代碼,將可以做的優(yōu)化都完成,但直譯語(yǔ)言就無(wú)法做到這一點(diǎn),因?yàn)閳?zhí)行時(shí)才開(kāi)始解釋的關(guān)係,執(zhí)行上就相對(duì)較慢,也沒(méi)辦法在一開(kāi)始做優(yōu)化,為了處理這個(gè)狀況,JIT 出現(xiàn)了。

JIT 結(jié)合解釋和編譯兩者,讓執(zhí)行 JavaScript 的時(shí)候,能夠分析代碼執(zhí)行過(guò)程的情報(bào),并在取得足夠情報(bào)時(shí),將相關(guān)的代碼再編譯成效能更快的機(jī)器碼。

聽(tīng)起來(lái) JIT 超讚,而在 V8 引擎裡負(fù)責(zé)處理 JIT 的左右手分別為 Ignition 和 **TurboFan**。

Ignition & TurboFan

成功解析出 AST 后,Ignition? 會(huì)將 AST? 解釋為 ByteCode?,成為可執(zhí)行的語(yǔ)言,但是 V8 引擎還未在這裡結(jié)束,Ignition 用 ByteCode 執(zhí)行的時(shí)候,會(huì)搜集代碼在執(zhí)行時(shí)的類型信息。舉個(gè)例子,如果我們有個(gè) sum? 函式,并且始終確定呼叫的參數(shù)類型都是 number,那麼 Ignition 會(huì)將它記錄起來(lái)。

此時(shí),在另一方面的 TurboFan 就會(huì)去查看這些信息,當(dāng)它確認(rèn)到“只有 number? 類型的參數(shù)會(huì)被送進(jìn) sum? 這個(gè)函式執(zhí)行”這個(gè)情報(bào)的時(shí)候,就會(huì)進(jìn)行 Optimization,把 sum 從 ByteCode 再編譯為更快的機(jī)器碼執(zhí)行。

如此一來(lái),就能夠保留 JavaScript 直譯語(yǔ)言的特性,又能夠在執(zhí)行的時(shí)候優(yōu)化性能。

但畢竟是 JavaScript,誰(shuí)也不敢保證第一百萬(wàn)零一次送進(jìn)來(lái)的參數(shù)仍然是 number?,因此當(dāng) sum 接收到的參數(shù)與之前 Optimization 的策略不同時(shí),就會(huì)進(jìn)行 Deoptimization 的動(dòng)作。

TurboFan 的 Optimization 并不是將原有的 ByteCode 直接變成機(jī)器碼,而是在產(chǎn)生機(jī)器碼的同時(shí),增加一個(gè) Checkpoint 到 ByteCode 和機(jī)器碼之間,在執(zhí)行機(jī)器碼之前,會(huì)先用 Checkpoint 檢查是否與先前 Optimization 的類型符合。這樣的話,當(dāng) sum 以與 Optimization 不同的類型被呼叫的時(shí)候,就會(huì)在 Checkpoint 這關(guān)被擋下來(lái),并進(jìn)行 Deoptimization。

最后如果 TurboFan 重複執(zhí)行了 5 次 Optimization 和 Deoptimization 的過(guò)程,就會(huì)直接放棄治療,不會(huì)再幫這個(gè)函式做 Optimization。

那到底該怎麼知道 TurboFan 有沒(méi)有真的做 Optimization 咧?我們可以用下方的代碼來(lái)做個(gè)實(shí)驗(yàn):

const loopCount = 10000000;
const sum = (a, b) => a + b;

performance.mark('first_start');

for (let i = 0; i < loopCount; i += 1) {
sum(1, i);
}

performance.mark('first_end');


performance.mark('second_start');

for (let i = 0; i < loopCount; i += 1) {
sum(1, i);
}

performance.mark('second_end');

performance.measure('first_measure', 'first_start', 'first_end');
const first_measures = performance.getEntriesByName('first_measure');
console.log(first_measures[0]);

performance.measure('second_measure', 'second_start', 'second_end');
const second_measures = performance.getEntriesByName('second_measure');
console.log(second_measures[0]);

上方利用 Node.js v18.1 的 perf_hooks 做執(zhí)行速度的測(cè)量,執(zhí)行結(jié)果如下:

圖片

執(zhí)行后會(huì)發(fā)現(xiàn)第一次執(zhí)行的時(shí)間花了 8 秒,第二次的執(zhí)行時(shí)間只花了 6 秒,大家可以再把 loopCount 的數(shù)字改大一點(diǎn),差距會(huì)越來(lái)越明顯。

但是這麼做仍然沒(méi)辦法確認(rèn)是 TurboFan 動(dòng)了手腳,因此接下來(lái)執(zhí)行的時(shí)候,加上 --trace-opt 的 flag,看看 Optimization 是否有發(fā)生:

圖片

執(zhí)行后的信息顯示了 TurboFan 做的幾次 Optimization,也有把每次 Optimization 的原因?qū)懴聛?lái),像第一二行分別顯示了原因?yàn)?hot and stable 和 small function,這些都是 TurboFan 背后做的 Optimization 策略。

那 Deoptimization 的部分呢?要測(cè)試也很簡(jiǎn)單,只要把第二個(gè)迴圈的參數(shù)型別改成 String 送給 sum 函式執(zhí)行,那 TurboFan 就會(huì)進(jìn)行 Deoptimization,為了查看 Deoptimization 的訊息,下方執(zhí)行的時(shí)候再加上 --trace-deopt:

圖片

在 highlight 的那一段,就是因?yàn)樗腿?sum 的參數(shù)型別不同,所以執(zhí)行了 Deoptimization,但是接下來(lái)又因?yàn)橐恢彼?String 進(jìn) sum 執(zhí)行,所以 TurboFan 又會(huì)再替 sum 重新做 Optimization。

總結(jié)

整理 V8 引擎執(zhí)行 JavaScript 的過(guò)程后,能夠得出下方的流程圖:

圖片

搭配上圖解說(shuō) V8 引擎如何執(zhí)行 JavaScript:

  • Parser 透過(guò) Scanner 的 Tokens 將 JavaScript 解析成 AST
  • Ignition 把 AST 解釋成 ByteCode 執(zhí)行,并且在執(zhí)行時(shí)搜集類型信息
  • TurboFan 針對(duì)信息將 ByteCode 再編譯為機(jī)器碼
  • 如果機(jī)器碼檢查到這次的執(zhí)行和之前 Optimization 策略不同,就做 Deop timization 回到 ByteCode,以繼續(xù)搜集類型信息或放棄治療。

作者:神Q超人 > 來(lái)源:medium

原文:https://medium.com/tarbugs/%E5%9F%B7%E8%A1%8C-javascript-%E7%9A%84-v8-%E5%BC%95%E6%93%8E%E5%81%9A%E4%BA%86%E4%BB%80%E9%BA%BC-f97e5b4b3fbe

作者:Andy Chen  

譯者:前端小智

來(lái)源:medium 

原文:https://medium.com/starbugs/%E5%8E%9F%E4%BE%86%E7%A8%8B%E5%BC%8F%E7%A2%BC%E6%89%93%E5%8C%85%E4%B9%9F%E6%9C%89%E9%80%99%E9%BA%BC%E5%A4%9A%E7%9C%89%E8%A7%92-%E6%B7%BA%E8%AB%87-tree-shaking-%E6%A9%9F%E5%88%B6-8375d35d87b2?

責(zé)任編輯:武曉燕 來(lái)源: 大遷世界
相關(guān)推薦

2022-09-16 08:32:25

JavaC++語(yǔ)言

2023-06-05 16:38:51

JavaScript編程語(yǔ)言V8

2020-10-12 06:35:34

V8JavaScript

2023-10-10 10:23:50

JavaScriptV8

2009-07-20 09:36:04

谷歌瀏覽器安全漏洞

2022-10-24 09:11:05

TypeScriptV8

2020-10-12 14:59:31

V8引擎如何執(zhí)行Jav

2017-12-17 16:34:18

JavaScript代碼V8

2020-09-27 07:32:18

V8

2022-06-21 08:52:47

Node.js服務(wù)端JavaScript

2022-02-25 08:32:07

nodemon搭Node.jsJavascript

2020-10-25 08:22:28

V8 引擎JavaScript回調(diào)函數(shù)

2022-11-04 07:12:24

JavaScript基準(zhǔn)測(cè)試

2019-05-28 10:24:31

V8JavaScript延遲

2009-08-21 10:09:02

Google ChroV8引擎linux系統(tǒng)

2010-07-20 16:35:52

V8JavaScript瀏覽器

2019-11-28 10:53:19

程序員技能開(kāi)發(fā)者

2023-06-07 16:00:40

JavaScriptV8語(yǔ)言

2016-04-18 09:33:52

nodejswebapp

2024-11-19 15:13:02

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)