成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

JavaScript工作原理(二):V8引擎和5招高效代碼

leone / 507人閱讀

摘要:引擎可以用標(biāo)準(zhǔn)解釋器或即時(shí)編譯器來實(shí)現(xiàn),即時(shí)編譯器以某種形式將代碼編譯為字節(jié)碼。這里的主要區(qū)別在于不生成字節(jié)碼或任何中間代碼。請(qǐng)注意,不使用中間字節(jié)碼表示法,不需要解釋器。這允許在正常執(zhí)行期間非常短的暫停。

本系列的第一篇文章重點(diǎn)介紹了引擎,運(yùn)行時(shí)調(diào)用棧的概述。第二篇文章將深入V8的JavaScript引擎的內(nèi)部。我們還會(huì)提供一些關(guān)于如何編寫更好的JavaScript代碼的技巧。

概述

JavaScript引擎是執(zhí)行JavaScript代碼的程序或解釋器。JavaScript引擎可以用標(biāo)準(zhǔn)解釋器(interpreter)或即時(shí)編譯器(just-in-time compiler)來實(shí)現(xiàn),即時(shí)編譯器以某種形式將JavaScript代碼編譯為字節(jié)碼。

流行的JavaScript引擎:

V8:開源,Google開發(fā),C++,Chrome瀏覽器

Rhino:開源,Mozilla開發(fā),Java

SpiderMonkey:第一個(gè)JavaScript引擎,網(wǎng)景瀏覽器(之前)和Firefox(現(xiàn)在)

JavaScriptCore:開源,蘋果Safari瀏覽器

Chakra(JSscript9):Internet Explorer瀏覽器

Chakra(JavaScript):Microsoft Edge瀏覽器

V8起源

V8引擎是由Google構(gòu)建的,用C++開發(fā)并且開源,與其它的引擎不同的是,V8還是Node.js的運(yùn)行時(shí)環(huán)境。

V8最初設(shè)計(jì)用于提高瀏覽器內(nèi)部JavaScript執(zhí)行的性能。為了獲得速度,V8將JavaScript代碼轉(zhuǎn)換為更高效的機(jī)器代碼(machine code),而不是使用解釋器。它通過實(shí)現(xiàn)JIT(Just-In-Time)編譯器(如SpiderMonkey或Rhino,等許多現(xiàn)代JavaScript引擎)將JavaScript代碼編譯為機(jī)器代碼。這里的主要區(qū)別在于V8不生成字節(jié)碼或任何中間代碼。

V8曾經(jīng)的兩個(gè)編譯器

在V8引擎的v5.9版本出來之前,V8有兩個(gè)編譯器:
full-codegen:一個(gè)簡單而且速度非??斓木幾g器,可以生成簡單且相對(duì)較慢的機(jī)器代碼。
Crankshaft:一種更復(fù)雜(Just-In-Time)的優(yōu)化編譯器,可以生成高度優(yōu)化的代碼。

V8引擎還在內(nèi)部使用多個(gè)線程:

主線程完成預(yù)定的任務(wù):獲取你的代碼,編譯它然后執(zhí)行它

一個(gè)多帶帶的線程用于編譯,當(dāng)這個(gè)多帶帶的線程優(yōu)化代碼時(shí),主線程可以繼續(xù)執(zhí)行

一個(gè)Profiler線程,它會(huì)告訴運(yùn)行時(shí)我們花了很多時(shí)間,使得Crankshaft能夠優(yōu)化它們

一些線程處理垃圾處理器掃描

當(dāng)?shù)谝淮螆?zhí)行JavaScript代碼時(shí),V8利用full-codegen,直接將解析的JavaScript翻譯成機(jī)器代碼而無需任何轉(zhuǎn)換。這使它可以非??焖俚亻_始執(zhí)行機(jī)器代碼。請(qǐng)注意,V8不使用中間字節(jié)碼表示法,不需要解釋器。

當(dāng)您的代碼運(yùn)行一段時(shí)間后,Profiler線程已經(jīng)收集了足夠的數(shù)據(jù)以確定哪種方法應(yīng)該進(jìn)行優(yōu)化。

接下來,Crankshaft優(yōu)化從另一個(gè)線程開始。它將JavaScript抽象語法樹翻譯為稱為Hydrogen的高級(jí)靜態(tài)單分配(SSA)表示,并嘗試優(yōu)化該hydrogen圖。大多數(shù)優(yōu)化都是在這個(gè)級(jí)別完成的。

優(yōu)化:內(nèi)聯(lián)

第一次優(yōu)化是提前盡可能多地嵌入代碼。 內(nèi)聯(lián)是將被調(diào)用函數(shù)的主體替換為調(diào)用網(wǎng)站(調(diào)用該函數(shù)的代碼行)的過程。 這個(gè)簡單的步驟可以讓以下優(yōu)化變得更有意義。

優(yōu)化:隱藏的類

JavaScript是一種基于原型的語言:沒有類,對(duì)象的創(chuàng)建是通過克隆實(shí)現(xiàn)的。JavaScript也是一種動(dòng)態(tài)編程語言,它意味著屬性可以在實(shí)例化后輕松添加或從對(duì)象中移除。

大多數(shù)JavaScript解釋器使用字典式結(jié)構(gòu)(基于哈希函數(shù))來存儲(chǔ)對(duì)象屬性值在內(nèi)存中的位置。這種結(jié)構(gòu)使得檢索JavaScript中的屬性的值比在Java或C#等非動(dòng)態(tài)編程語言中的計(jì)算更昂貴。在Java中,所有對(duì)象屬性都是在編譯之前由固定的對(duì)象布局確定的,并且不能在運(yùn)行時(shí)動(dòng)態(tài)添加或刪除(當(dāng)然,C#的動(dòng)態(tài)類型是另一個(gè)主題)。因此,屬性的值(或指向這些屬性的指針)可以作為連續(xù)緩沖區(qū)存儲(chǔ)在內(nèi)存中,每個(gè)值之間都有一個(gè)固定偏移量。偏移量的長度可以根據(jù)屬性類型輕松確定,但在運(yùn)行時(shí)可以更改屬性類型的JavaScript中不可行。

由于使用字典查找內(nèi)存中對(duì)象屬性的位置效率非常低,因此V8使用不同的方法:隱藏類。隱藏類的工作方式與Java等語言中使用的固定對(duì)象布局(類)類似,除了它們是在運(yùn)行時(shí)創(chuàng)建的?,F(xiàn)在,讓我們看看他們實(shí)際的樣子:

function Point(x, y) {
    this.x = x;
    this.y = y;
}
var p1 = new Point(1, 2);

當(dāng)“new Point(1, 2)”被執(zhí)行時(shí), V8引擎會(huì)創(chuàng)建一個(gè)名為C0的隱藏類。

由于Point還未定義任何屬性,因此“C0”為空。

一旦執(zhí)行了第一條語句“this.x = x”(在“Point”函數(shù)內(nèi)部),V8將創(chuàng)建第二個(gè)隱藏類“C1”,它基于“C0”?!癈1”描述了可以找到屬性x的存儲(chǔ)器中的位置(相對(duì)于對(duì)象指針)。在這種情況下,“x”存儲(chǔ)在偏移量0處,這意味著在內(nèi)存中將點(diǎn)對(duì)象視為連續(xù)緩沖區(qū)時(shí),第一個(gè)偏移量將對(duì)應(yīng)于屬性“x”。 V8還將用“類別轉(zhuǎn)換”更新“C0”,該類別轉(zhuǎn)換指出如果將屬性“x”添加到點(diǎn)對(duì)象,隱藏類應(yīng)從“C0”切換到“C1”。 下面的點(diǎn)對(duì)象的隱藏類現(xiàn)在是“C1”。

每次將新屬性添加到對(duì)象時(shí),舊的隱藏類都會(huì)使用到新隱藏類的轉(zhuǎn)換路徑進(jìn)行更新。隱藏類轉(zhuǎn)換非常重要,因?yàn)樗鼈冊(cè)试S隱藏類在以相同方式創(chuàng)建的對(duì)象之間共享。如果兩個(gè)對(duì)象共享一個(gè)隱藏類并向它們添加了相同的屬性,則轉(zhuǎn)換將確保兩個(gè)對(duì)象都接收到相同的新隱藏類以及隨附的所有優(yōu)化代碼。

當(dāng)執(zhí)行語句“this.y = y”(同樣,在“this.x = x”語句之后的Point函數(shù)內(nèi)部)時(shí),將重復(fù)此過程。

創(chuàng)建一個(gè)名為“C2”的新隱藏類,將類轉(zhuǎn)換添加到“C1”,指出如果將屬性“y”添加到Point對(duì)象(已包含屬性“x”),則隱藏類應(yīng)更改為 “C2”,點(diǎn)對(duì)象的隱藏類更新為“C2”。

隱藏類轉(zhuǎn)換取決于將屬性添加到對(duì)象的順序。 看看下面的代碼片段:

function Point(x, y) {
    this.x = x;
    this.y = y;
}

var p1 = new Point(1, 2);
p1.a = 5;
p1.b = 6;

var p2 = new Point(3, 5);
p2.b = 7;
p2.a = 8; 

現(xiàn)在,您可能認(rèn)為對(duì)于p1和p2,將使用相同的隱藏類和轉(zhuǎn)換。事實(shí)上卻不是。對(duì)于“p1”,首先添加屬性“a”,然后添加屬性“b”。然而,對(duì)于“p2”,首先分配“b”,然后是“a”。 因此,由于不同的轉(zhuǎn)換路徑,“p1”和“p2”以不同的隱藏類結(jié)束。在這種情況下,以相同順序初始化動(dòng)態(tài)屬性好得多,以便隱藏的類可以重用。

內(nèi)聯(lián)緩存

V8利用另一種技術(shù)來優(yōu)化稱為內(nèi)聯(lián)緩存的動(dòng)態(tài)類型化語言。內(nèi)聯(lián)緩存依賴于觀察到對(duì)相同方法的重復(fù)調(diào)用傾向于發(fā)生在相同類型的對(duì)象上。在這里可以找到關(guān)于內(nèi)聯(lián)緩存的深入解釋。

我們將討論內(nèi)聯(lián)緩存的一般概念(如果您沒有時(shí)間通過??上面的深入解釋)。

那么它是怎樣工作的? V8維護(hù)一個(gè)對(duì)象類型的緩存,這些對(duì)象在最近的方法調(diào)用中作為參數(shù)傳遞,并使用這些信息來預(yù)測(cè)將來作為參數(shù)傳遞的對(duì)象的類型。如果V8能夠?qū)鬟f給方法的對(duì)象的類型做出很好的假設(shè),那么它可以繞過確定如何訪問對(duì)象屬性的過程,而是使用以前查找存儲(chǔ)的信息到對(duì)象的隱藏課程。

那么隱藏類和內(nèi)聯(lián)緩存的概念如何相關(guān)?無論何時(shí)在特定對(duì)象上調(diào)用方法,V8引擎都必須執(zhí)行對(duì)該對(duì)象的隱藏類的查找,以確定訪問特定屬性的偏移量。在相同隱藏類的兩次成功調(diào)用之后,V8省略了隱藏類查找,并簡單地將該屬性的偏移量添加到對(duì)象指針本身。對(duì)于該方法的所有未來調(diào)用,V8引擎都假定隱藏的類沒有更改,并使用從以前的查找存儲(chǔ)的偏移量直接跳轉(zhuǎn)到特定屬性的內(nèi)存地址。這大大提高了執(zhí)行速度。

內(nèi)聯(lián)緩存也是為什么相同類型的對(duì)象共享隱藏類非常重要的原因。如果您創(chuàng)建兩個(gè)具有相同類型和不同隱藏類的對(duì)象(就像我們之前的示例中那樣),V8將無法使用內(nèi)聯(lián)緩存,因?yàn)榧词惯@兩個(gè)對(duì)象的類型相同,它們對(duì)應(yīng)的隱藏類為其屬性分配不同的偏移量。

編譯為機(jī)器碼

一旦Hydrogen圖被優(yōu)化,Crankshaft將其降低到稱為Lithium的較低級(jí)表示。大部分的Lithium實(shí)施都是特定于架構(gòu)的。寄存器分配發(fā)生在這個(gè)級(jí)別。

最終,Lithium被編譯成機(jī)器碼。然后發(fā)生其他事情,稱為OSR:堆棧替換。在我們開始編譯和優(yōu)化那些耗時(shí)較長的方法之前,我們可能會(huì)運(yùn)行它。V8不會(huì)忘記它剛剛緩慢執(zhí)行的內(nèi)容,以再次優(yōu)化版本開始。相反,它會(huì)轉(zhuǎn)換我們擁有的所有上下文(堆棧,寄存器),以便我們可以在執(zhí)行過程中切換到優(yōu)化版本。這是一項(xiàng)非常復(fù)雜的任務(wù),考慮到除了其他優(yōu)化之外,V8最初還是將代碼內(nèi)聯(lián)。 V8不是唯一能夠做到的引擎。

有一種叫做去最佳化的保護(hù)措施可以做出相反的轉(zhuǎn)變,并在引擎的假設(shè)不再成立的情況下恢復(fù)到非優(yōu)化的代碼。

垃圾收集

對(duì)于垃圾收集,V8采用了傳統(tǒng)的標(biāo)記清除方式來清理老一代。標(biāo)記階段應(yīng)該停止JavaScript執(zhí)行。為了控制GC成本并使執(zhí)行更加穩(wěn)定,V8使用增量標(biāo)記:不是遍歷整個(gè)堆,而是試圖標(biāo)記每個(gè)可能的對(duì)象,它只走過堆的一部分,然后恢復(fù)正常執(zhí)行。下一個(gè)GC停止將從先前堆走過的地方繼續(xù)。這允許在正常執(zhí)行期間非常短的暫停。如前所述,掃描階段由多帶帶的線程處理。

Ignition和TurboFan

隨著2017年早些時(shí)候發(fā)布V8 5.9,引入了新的執(zhí)行流程。這個(gè)新的管道在實(shí)際的JavaScript應(yīng)用程序中實(shí)現(xiàn)了更大的性能改進(jìn)和顯著的內(nèi)存節(jié)省。

新的執(zhí)行流程建立在Ignition,V8的解釋器和TurboFan,V8的最新優(yōu)化編譯器之上。

您可以查看V8團(tuán)隊(duì)關(guān)于此主題的博客文章。

自從V8.5版本問世以來,V8團(tuán)隊(duì)一直在努力跟上新的JavaScript語言特性,而V8團(tuán)隊(duì)已經(jīng)不再使用V8版本的full-codegen和Crankshaft(自2010年以來服務(wù)于V8的技術(shù))。這些功能需要進(jìn)行優(yōu)化。

這意味著整體V8將有更簡單和更可維護(hù)的架構(gòu)。

這些改進(jìn)僅僅是一個(gè)開始。 新的Ignition和TurboFan管道為進(jìn)一步優(yōu)化鋪平了道路,這將在未來幾年提升JavaScript性能并縮小V8在Chrome和Node.js中的占用空間。

最后,這里有一些關(guān)于如何編寫優(yōu)化的,更好的JavaScript的技巧和竅門。 您可以輕松地從上述內(nèi)容中獲取這些內(nèi)容,但是,為了方便起見,以下是摘要:

如何編寫優(yōu)化的JavaScript

對(duì)象屬性的順序:始終以相同的順序?qū)嵗瘜?duì)象屬性,以便可以共享隱藏類和隨后優(yōu)化的代碼。

動(dòng)態(tài)屬性:在實(shí)例化之后向?qū)ο筇砑訉傩詫?qiáng)制隱藏類更改,并減慢為先前隱藏類優(yōu)化的所有方法。相反,在其構(gòu)造函數(shù)中分配所有對(duì)象的屬性。

方法:重復(fù)執(zhí)行相同方法的代碼將比僅執(zhí)行一次(由于內(nèi)聯(lián)緩存)執(zhí)行許多不同方法的代碼運(yùn)行得更快。

數(shù)組:避免稀疏數(shù)組,其中的鍵不是增量數(shù)字。稀疏數(shù)組中沒有每個(gè)元素都是哈希表。這種陣列中的元素訪問費(fèi)用較高。另外,盡量避免預(yù)分配大型數(shù)組。隨著你的成長,成長會(huì)更好。最后,不要?jiǎng)h除數(shù)組中的元素。它使密鑰稀疏。

標(biāo)記值:V8用32位來表示對(duì)象和數(shù)字。由于它的31位,它使用1個(gè)bit來知道它是一個(gè)對(duì)象(flag = 1)還是一個(gè)稱為SMI(SMall Integer)的整數(shù)(flag = 0)。然后,如果數(shù)字值大于31位,V8會(huì)將該數(shù)字框起來,將其變成雙精度值并創(chuàng)建一個(gè)新對(duì)象以將該數(shù)字放入其中。嘗試盡可能使用31位有符號(hào)數(shù)字以避免將昂貴的裝箱操作轉(zhuǎn)換為JS對(duì)象。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/94588.html

相關(guān)文章

  • JavaScript工作原理(一):引擎,運(yùn)行時(shí),調(diào)用堆棧

    摘要:調(diào)用棧是一種單線程編程語言,這意味著它只有一個(gè)調(diào)用棧。這就是調(diào)用棧的功能。簡單代碼示例當(dāng)引擎執(zhí)行這段代碼時(shí),調(diào)用棧為空,之后運(yùn)行如下每個(gè)叫做堆棧幀。調(diào)用棧就是通過堆棧幀來追蹤異常,堆棧幀基本就是調(diào)用棧出現(xiàn)異常時(shí)候的狀態(tài)。 概述 幾乎每個(gè)人都已經(jīng)聽說過V8引擎這個(gè)概念,而且大多人都知道JavaScript是單線程的,并且使用回調(diào)隊(duì)列。 這篇文章中,我們將詳細(xì)介紹這些概念,并解釋JavaS...

    Jingbin_ 評(píng)論0 收藏0
  • javascript引擎——V8

    摘要:類將源代碼解釋并構(gòu)建成抽象語法樹,使用類來創(chuàng)建它們,并使用類來分配內(nèi)存。類抽象語法樹的訪問者類,主要用來遍歷抽象語法樹。在該函數(shù)中,先使用類來生成抽象語法樹再使用類來生成本地代碼。 通過上一篇文章,我們知道了JavaScript引擎是執(zhí)行JavaScript代碼的程序或解釋器,了解了JavaScript引擎的基本工作原理。我們經(jīng)常聽說的JavaScript引擎就是V8引擎,這篇文章我們...

    luoyibu 評(píng)論0 收藏0
  • JavaScript 工作原理-如何在 V8 引擎中書寫最優(yōu)代碼5 條小技巧(譯)

    摘要:本章將會(huì)深入谷歌引擎的內(nèi)部結(jié)構(gòu)。一個(gè)引擎可以用標(biāo)準(zhǔn)解釋程序或者即時(shí)編譯器來實(shí)現(xiàn),即時(shí)編譯器即以某種形式把解釋為字節(jié)碼。引擎的由來引擎是由谷歌開源并以語言編寫。注意到?jīng)]有使用中間字節(jié)碼來表示,這樣就不需要解釋器了。 原文請(qǐng)查閱這里,略有刪減。 本系列持續(xù)更新中,Github 地址請(qǐng)查閱這里。 這是 JavaScript 工作原理的第二章。 本章將會(huì)深入谷歌 V8 引擎的內(nèi)部結(jié)構(gòu)。我們也會(huì)...

    PingCAP 評(píng)論0 收藏0
  • [譯文] JavaScript工作原理V8引擎內(nèi)部+5條優(yōu)化代碼的竅門

    摘要:本文將會(huì)深入分析的引擎的內(nèi)部實(shí)現(xiàn)。該引擎使用在谷歌瀏覽器內(nèi)部。同其他現(xiàn)代引擎如或所做的一樣,通過實(shí)現(xiàn)即時(shí)編譯器在執(zhí)行時(shí)將代碼編譯成機(jī)器代碼。這可使正常執(zhí)行期間只發(fā)生相當(dāng)短的暫停。 原文 How JavaScript works: inside the V8 engine + 5 tips on how to write optimized code 幾周前我們開始了一個(gè)系列博文旨在深入...

    dreamans 評(píng)論0 收藏0
  • JavaScript深入淺出第5課:Chrome是如何成功的?

    摘要:所做的最重要的事情,就是對(duì)成千上萬的網(wǎng)頁進(jìn)行排序,所以它存在的意義是基于網(wǎng)頁的。確實(shí)有很多非常成功的產(chǎn)品,比如,,,但是它們其實(shí)都是收購來的。為什么呢因?yàn)橐龅綐O簡主義,需要深刻思考用戶需求以及產(chǎn)品價(jià)值。 摘要: Chrome改變世界。 《JavaScript深入淺出》系列: JavaScript深入淺出第1課:箭頭函數(shù)中的this究竟是什么鬼? JavaScript深入淺出第2課:...

    luqiuwen 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<