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

資訊專(zhuān)欄INFORMATION COLUMN

精讀《V8 引擎 Lazy Parsing》

羅志環(huán) / 2328人閱讀

摘要:在執(zhí)行函數(shù)時(shí),通過(guò)保存堆棧狀態(tài),再保存堆棧跳出后返回位置的指針,最后對(duì)變量賦值。這看上去沒(méi)有問(wèn)題,只要將值存在堆棧就搞定了。

1. 引言

本周精讀的文章是 V8 引擎 Lazy Parsing,看看 V8 引擎為了優(yōu)化性能,做了怎樣的嘗試吧!

這篇文章介紹的優(yōu)化技術(shù)叫 preparser,是通過(guò)跳過(guò)不必要函數(shù)編譯的方式優(yōu)化性能。

2. 概述 & 精讀

解析 Js 發(fā)生在網(wǎng)頁(yè)運(yùn)行的關(guān)鍵路徑上,因此加速對(duì) JS 的解析,就可以加速網(wǎng)頁(yè)運(yùn)行效率。

然而并不是所有 Js 都需要在初始化時(shí)就被執(zhí)行,因此也不需要在初始化時(shí)就解析所有的 Js!因?yàn)榫幾g Js 會(huì)帶來(lái)三個(gè)成本問(wèn)題:

編譯不必要的代碼會(huì)占用 CPU 資源。

在 GC 前會(huì)占用不必要的內(nèi)存空間。

編譯后的代碼會(huì)緩存在磁盤(pán),占用磁盤(pán)空間。

因此所有主流瀏覽器都實(shí)現(xiàn)了 Lazy Parsing(延遲解析),它會(huì)將不必要的函數(shù)進(jìn)行預(yù)解析,也就是只解析出外部函數(shù)需要的內(nèi)容,而全量解析在調(diào)用這個(gè)函數(shù)時(shí)才發(fā)生。

預(yù)解析的挑戰(zhàn)

本來(lái)預(yù)解析也不難,因?yàn)橹灰袛嘁粋€(gè)函數(shù)是否會(huì)立即執(zhí)行就可以了,只有立即執(zhí)行的函數(shù)才需要被完全解析。

使得預(yù)解析變復(fù)雜的是變量分配問(wèn)題。原文通過(guò)了堆棧調(diào)用的例子說(shuō)明原因:

Js 代碼的執(zhí)行在堆棧上完成,比如下面這個(gè)函數(shù):

function f(a, b) {
  const c = a + b;
  return c;
}

function g() {
  return f(1, 2);
  // The return instruction pointer of `f` now points here
  // (because when `f` `return`s, it returns here).
}

這段函數(shù)的調(diào)用堆棧如下:

需要?jiǎng)?chuàng)建一個(gè) context 存儲(chǔ)函數(shù) f 中變量 d 的值。

也就是說(shuō),如果一個(gè)在函數(shù)內(nèi)部定義的變量被子 Scope 使用時(shí),Js 引擎需要識(shí)別這種情況,并將這個(gè)變量值存儲(chǔ)在 context 中。

所以對(duì)于函數(shù)定義的每一個(gè)入?yún)?,我們需要知道其是否?huì)被子函數(shù)引用。也就是說(shuō),在 preparser 階段,我們只要少能分析出哪些變量被內(nèi)部函數(shù)引用了。

難以分辨的引用

預(yù)處理器中跟蹤變量的申明與引用很復(fù)雜,因?yàn)?Js 的語(yǔ)法導(dǎo)致了無(wú)法從部分表達(dá)式推斷含義,比如下面的函數(shù):

function f(d) {
  function g() {
    const a = ({ d }

我們不清楚第三行的 d 到底是不是指代第一行的 d。它可能是:

function f(d) {
  function g() {
    const a = ({ d } = { d: 42 });
    return a;
  }
  return g;
}

也可能只是一個(gè)自定義函數(shù)參數(shù),與上面的 d 無(wú)關(guān):

function f(d) {
  function g() {
    const a = ({ d }) => d;
    return a;
  }

  return [d, g];
}
惰性 parse

在執(zhí)行函數(shù)時(shí),只會(huì)將最外層執(zhí)行的函數(shù)完全編譯并生成 AST,而對(duì)內(nèi)部模塊只進(jìn)行 preparser

// This is the top-level scope.
function outer() {
  // preparsed
  function inner() {
    // preparsed
  }
}

outer(); // Fully parses and compiles `outer`, but not `inner`.

為了允許惰性編譯函數(shù),上下文指針指向了 ScopeInfo 的對(duì)象(從代碼中可以看到,ScopeInfo 包含上下文信息,比如當(dāng)前上下文是否有函數(shù)名,是否在一個(gè)函數(shù)內(nèi)等等),當(dāng)編譯內(nèi)部函數(shù)時(shí),可以利用 ScopeInfo 繼續(xù)編譯子函數(shù)。

但是為了判斷惰性編譯函數(shù)自身是否需要一個(gè)上下文,我們需要再次解析內(nèi)部的函數(shù):比如我們需要知道某個(gè)子函數(shù)是否對(duì)外層函數(shù)定義的變量有所引用。

這樣就會(huì)產(chǎn)生遞歸遍歷:

由于代碼總會(huì)包含一些嵌套,而編譯工具更會(huì)產(chǎn)生 IIFE(立即調(diào)用函數(shù)) 這種多層嵌套的表達(dá)式,使得遞歸性能比較差。

而下面有一種辦法可以將時(shí)間復(fù)雜度簡(jiǎn)化為線性:將變量分配的位置序列化為一個(gè)密集的數(shù)組,當(dāng)惰性解析函數(shù)時(shí),變量會(huì)按照原先的順序重新創(chuàng)建,這樣就不需要因?yàn)樽雍瘮?shù)可能引用外層定義變量的原因,對(duì)所有子函數(shù)進(jìn)行遞歸惰性解析了。

按照這種方式優(yōu)化后的時(shí)間復(fù)雜度是線性的:

針對(duì)模塊化打包的優(yōu)化

由于現(xiàn)代代碼幾乎都是模塊化編寫(xiě)的,構(gòu)建起在打包時(shí)會(huì)將模塊化代碼封裝在 IIFE(立即調(diào)用的閉包)中,以保證模擬模塊化環(huán)境運(yùn)行。比如 (function(){....})()。

這些代碼看似在函數(shù)中應(yīng)該惰性編譯,但其實(shí)這些模塊化代碼從一開(kāi)始就要被編譯,否則反而會(huì)影響性能,因此 V8 有兩種機(jī)制識(shí)別這些可能被立即調(diào)用的函數(shù):

如果函數(shù)是帶括號(hào)的,比如 (function(){...}),就假設(shè)它會(huì)被立即調(diào)用。

從 V8 v5.7 / Chrome 57 開(kāi)始,還會(huì)識(shí)別 uglifyJS 的 !function(){...}(), function(){...}(), function(){...}() 這種模式。

然而在瀏覽器引擎解析環(huán)境比較復(fù)雜,很難對(duì)函數(shù)進(jìn)行完整字符串匹配,因此只能對(duì)函數(shù)頭進(jìn)行簡(jiǎn)單判斷。所以對(duì)于下面這種匿名函數(shù)的行為,瀏覽器是不識(shí)別的:

// pre-parser
function run(func) {
  func()
}

run(function(){}) // 在這執(zhí)行它,進(jìn)行 full parser

上面的代碼看上去沒(méi)毛病,但由于瀏覽器只檢測(cè)被括號(hào)括住的函數(shù),因此這個(gè)函數(shù)不被認(rèn)為是立即執(zhí)行函數(shù),因此在后續(xù)執(zhí)行時(shí)會(huì)被重復(fù) full-parse。

也有一些代碼輔助轉(zhuǎn)換工具幫助 V8 正確識(shí)別,比如 optimize-js,會(huì)將代碼做如下轉(zhuǎn)換。

轉(zhuǎn)換前:

!function (){}()
function runIt(fun){ fun() }
runIt(function (){})

轉(zhuǎn)換后:

!(function (){})()
function runIt(fun){ fun() }
runIt((function (){}))

然而在 V8 v7.5+ 已經(jīng)很大程度解決了這個(gè)問(wèn)題,因此現(xiàn)在其實(shí)不需要使用 optimize-js 這種庫(kù)了~

4. 總結(jié)

JS 解析引擎在性能優(yōu)化做了不少工作,但同時(shí)也要應(yīng)對(duì)代碼編譯器產(chǎn)生的特殊 IIFE 閉包,防止對(duì)這種立即執(zhí)行閉包進(jìn)行重復(fù) parser。

最后,不要試圖總是將函數(shù)用括號(hào)括起來(lái),因?yàn)檫@樣會(huì)導(dǎo)致惰性編譯的特性無(wú)法啟用。

討論地址是:精讀《V8 引擎 Lazy Parsing》 · Issue #148 · dt-fe/weekly

如果你想?yún)⑴c討論,請(qǐng) 點(diǎn)擊這里,每周都有新的主題,周末或周一發(fā)布。前端精讀 - 幫你篩選靠譜的內(nèi)容。

關(guān)注 前端精讀微信公眾號(hào)

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

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

相關(guān)文章

  • 精讀《Nodejs V12》

    摘要:更好的安全性隨著的發(fā)布,從升級(jí)到了,更安全且更易配置。通過(guò)使用,程序可以減少握手所需時(shí)間來(lái)提升請(qǐng)求性能。提供診斷報(bào)告有一項(xiàng)實(shí)驗(yàn)功能,根據(jù)用戶需求提供診斷報(bào)告,包括崩潰性能下降內(nèi)存泄露使用高等等。前端精讀幫你篩選靠譜的內(nèi)容。 1. 引言 Node12 發(fā)布有幾個(gè)月了,讓我們跟隨 Nodejs 12 一起看看 Node12 帶來(lái)了哪些改變。 2. 概述 Node12 與以往的版本不同,帶來(lái)...

    CoderStudy 評(píng)論0 收藏0
  • 精讀《React 的多態(tài)性》

    摘要:引言本周精讀的文章是,看看作者是如何解釋這個(gè)多態(tài)性含義的。讀完文章才發(fā)現(xiàn),文章標(biāo)題改為的多態(tài)性更妥當(dāng),因?yàn)檎恼露荚谡f(shuō),而使用場(chǎng)景不局限于。更多討論討論地址是精讀的多態(tài)性如果你想?yún)⑴c討論,請(qǐng)點(diǎn)擊這里,每周都有新的主題,周末或周一發(fā)布。 1 引言 本周精讀的文章是:surprising-polymorphism-in-react-applications,看看作者是如何解釋這個(gè)多態(tài)性含...

    tabalt 評(píng)論0 收藏0
  • 精讀《JS 引擎基礎(chǔ)之 Shapes and Inline Caches》

    摘要:概述的解釋器優(yōu)化器代碼可能在字節(jié)碼或者優(yōu)化后的機(jī)器碼狀態(tài)下執(zhí)行,而生成字節(jié)碼速度很快,而生成機(jī)器碼就要慢一些了。比如有一個(gè)函數(shù),從獲取值引擎生成的字節(jié)碼結(jié)構(gòu)是這樣的指令是獲取參數(shù)指向的對(duì)象,并存儲(chǔ)在,第二步則返回。 1 引言 本期精讀的文章是:JS 引擎基礎(chǔ)之 Shapes and Inline Caches 一起了解下 JS 引擎是如何運(yùn)作的吧! JS 的運(yùn)作機(jī)制可以分為 AST 分...

    Tecode 評(píng)論0 收藏0
  • The Cost Of JavaScript 2018 精讀

    摘要:目前我們的業(yè)務(wù)項(xiàng)目采用的來(lái)進(jìn)行優(yōu)化和首屏性能提升??勺冃孕枰岄_(kāi)發(fā)人員降低開(kāi)發(fā)時(shí)的基準(zhǔn)線,來(lái)保證每一個(gè)用戶的體驗(yàn)。對(duì)于路由的切分以及庫(kù)的引入來(lái)說(shuō),這一個(gè)原則至關(guān)重要。快速生成一份站點(diǎn)的性能審查報(bào)告。 The Cost Of JavaScript 2018 關(guān)于原文 原文是在Medium上面看到的,Chrome工程師Addy Osmani發(fā)布的一篇文章,這位的Medium上面的自我介紹里...

    lushan 評(píng)論0 收藏0
  • 精讀《Scheduling in React》

    摘要:調(diào)度系統(tǒng),支持不同渲染優(yōu)先級(jí),對(duì)進(jìn)行調(diào)度。調(diào)度帶來(lái)的限制調(diào)度系統(tǒng)也存在兩個(gè)問(wèn)題。調(diào)度系統(tǒng)能力有限,只能在瀏覽器提供的能力范圍內(nèi)進(jìn)行調(diào)度,而無(wú)法影響比如的渲染回收周期。精讀關(guān)于調(diào)度系統(tǒng)的剖析,可以讀深入剖析這篇文章,感謝我們團(tuán)隊(duì)的淡蒼提供。 1. 引言 這次介紹的文章是 scheduling-in-react,簡(jiǎn)單來(lái)說(shuō)就是 React 的調(diào)度系統(tǒng),為了得到更順滑的用戶體驗(yàn)。 畢竟前端做到...

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

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

0條評(píng)論

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