亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

通過動(dòng)圖形象地為你介紹 Flexbox 是如何工作的(二)

alanoddsoff / 1848人閱讀

摘要:解釋器的利弊解釋器啟動(dòng)和執(zhí)行的更快。正是因?yàn)檫@個(gè)原因,解釋器看起來更加適合。這就是為什么最開始的瀏覽器都是用解釋器的原因。可是當(dāng)你運(yùn)行同樣的代碼一次以上的時(shí)候,解釋器的弊處就顯現(xiàn)出來了。起初,監(jiān)視器監(jiān)視著所有通過解釋器的代碼。

作者:Lin Clark

編譯:胡子大哈

翻譯原文:http://huziketang.com/blog/posts/detail?postId=58c12f36a6d8a07e449fdd22

英文原文:A crash course in just-in-time (JIT) compilers

轉(zhuǎn)載請(qǐng)注明出處,保留原文鏈接以及作者信息

本文是關(guān)于 WebAssembly 系列的第二篇文章。如果你沒有讀先前文章的話,建議先讀這里。如果對(duì) WebAssembly 沒概念,建議先讀這里(中文文章)。

JavaScript 的啟動(dòng)比較緩慢,但是通過 JIT 可以使其變快,那么 JIT 是如何起作用的呢?

JavaScript 在瀏覽器中是如何運(yùn)行的?

如果是你一個(gè)開發(fā)者,當(dāng)你決定在你的頁(yè)面中使用 JavaScript 的時(shí)候,有兩個(gè)要考慮的事情:目標(biāo)和問題。

目標(biāo):告訴計(jì)算機(jī)你想做什么。

問題:你和計(jì)算機(jī)說不同的語(yǔ)言,無法溝通。

你說的是人類的語(yǔ)言,而計(jì)算機(jī)用的是機(jī)器語(yǔ)言。機(jī)器語(yǔ)言也是一種語(yǔ)言,只是 JavaScript 或者其他高級(jí)編程語(yǔ)言機(jī)器能看得懂,而人類不用他們來交流罷了。它們是基于人類認(rèn)知而設(shè)計(jì)出來的。

所以呢,JavaScript 引擎的工作就是把人類的語(yǔ)言轉(zhuǎn)換成機(jī)器能看懂的語(yǔ)言。

這就像電影《降臨》中,人類和外星人的互相交流一樣。

在電影里面,人類和外星人不僅僅是語(yǔ)言不同,兩個(gè)群體看待世界的方式都是不一樣的。其實(shí)人類和機(jī)器也是類似(后面我會(huì)詳細(xì)介紹)。

那么翻譯是如何進(jìn)行的呢?

在代碼的世界中,通常有兩種方式來翻譯機(jī)器語(yǔ)言:解釋器和編譯器。

如果是通過解釋器,翻譯是一行行地邊解釋邊執(zhí)行

編譯器是把源代碼整個(gè)編譯成目標(biāo)代碼,執(zhí)行時(shí)不再需要編譯器,直接在支持目標(biāo)代碼的平臺(tái)上運(yùn)行。

這兩種翻譯的方式都各有利弊。

解釋器的利弊

解釋器啟動(dòng)和執(zhí)行的更快。你不需要等待整個(gè)編譯過程完成就可以運(yùn)行你的代碼。從第一行開始翻譯,就可以依次繼續(xù)執(zhí)行了。

正是因?yàn)檫@個(gè)原因,解釋器看起來更加適合 JavaScript。對(duì)于一個(gè) Web 開發(fā)人員來講,能夠快速執(zhí)行代碼并看到結(jié)果是非常重要的。

這就是為什么最開始的瀏覽器都是用 JavaScript 解釋器的原因。

可是當(dāng)你運(yùn)行同樣的代碼一次以上的時(shí)候,解釋器的弊處就顯現(xiàn)出來了。比如你執(zhí)行一個(gè)循環(huán),那解釋器就不得不一次又一次的進(jìn)行翻譯,這是一種效率低下的表現(xiàn)。

編譯器的利弊

編譯器的問題則恰好相反。

它需要花一些時(shí)間對(duì)整個(gè)源代碼進(jìn)行編譯,然后生成目標(biāo)文件才能在機(jī)器上執(zhí)行。對(duì)于有循環(huán)的代碼執(zhí)行的很快,因?yàn)樗恍枰貜?fù)的去翻譯每一次循環(huán)。

另外一個(gè)不同是,編譯器可以用更多的時(shí)間對(duì)代碼進(jìn)行優(yōu)化,以使的代碼執(zhí)行的更快。而解釋器是在 runtime 時(shí)進(jìn)行這一步驟的,這就決定了它不可能在翻譯的時(shí)候用很多時(shí)間進(jìn)行優(yōu)化。

Just-in-time 編譯器:綜合了兩者的優(yōu)點(diǎn)

為了解決解釋器的低效問題,后來的瀏覽器把編譯器也引入進(jìn)來,形成混合模式。

不同的瀏覽器實(shí)現(xiàn)這一功能的方式不同,不過其基本思想是一致的。在 JavaScript 引擎中增加一個(gè)監(jiān)視器(也叫分析器)。監(jiān)視器監(jiān)控著代碼的運(yùn)行情況,記錄代碼一共運(yùn)行了多少次、如何運(yùn)行的等信息。

起初,監(jiān)視器監(jiān)視著所有通過解釋器的代碼。

如果同一行代碼運(yùn)行了幾次,這個(gè)代碼段就被標(biāo)記成了 “warm”,如果運(yùn)行了很多次,則被標(biāo)記成 “hot”。

基線編譯器

如果一段代碼變成了 “warm”,那么 JIT 就把它送到編譯器去編譯,并且把編譯結(jié)果存儲(chǔ)起來。

代碼段的每一行都會(huì)被編譯成一個(gè)“樁”(stub),同時(shí)給這個(gè)樁分配一個(gè)以“行號(hào) + 變量類型”的索引。如果監(jiān)視器監(jiān)視到了執(zhí)行同樣的代碼和同樣的變量類型,那么就直接把這個(gè)已編譯的版本 push 出來給瀏覽器。

通過這樣的做法可以加快執(zhí)行速度,但是正如前面我所說的,編譯器還可以找到更有效地執(zhí)行代碼的方法,也就是做優(yōu)化。

基線編譯器可以做一部分這樣的優(yōu)化(下面我會(huì)給出例子),不過基線編譯器優(yōu)化的時(shí)間不能太久,因?yàn)闀?huì)使得程序的執(zhí)行在這里 hold 住。

不過如果代碼確實(shí)非常 “hot”(也就是說幾乎所有的執(zhí)行時(shí)間都耗費(fèi)在這里),那么花點(diǎn)時(shí)間做優(yōu)化也是值得的。

優(yōu)化編譯器

如果一個(gè)代碼段變得 “very hot”,監(jiān)視器會(huì)把它發(fā)送到優(yōu)化編譯器中。生成一個(gè)更快速和高效的代碼版本出來,并且存儲(chǔ)之。

為了生成一個(gè)更快速的代碼版本,優(yōu)化編譯器必須做一些假設(shè)。例如,它會(huì)假設(shè)由同一個(gè)構(gòu)造函數(shù)生成的實(shí)例都有相同的形狀——就是說所有的實(shí)例都有相同的屬性名,并且都以同樣的順序初始化,那么就可以針對(duì)這一模式進(jìn)行優(yōu)化。

整個(gè)優(yōu)化器起作用的鏈條是這樣的,監(jiān)視器從他所監(jiān)視代碼的執(zhí)行情況做出自己的判斷,接下來把它所整理的信息傳遞給優(yōu)化器進(jìn)行優(yōu)化。如果某個(gè)循環(huán)中先前每次迭代的對(duì)象都有相同的形狀,那么就可以認(rèn)為它以后迭代的對(duì)象的形狀都是相同的??墒菍?duì)于 JavaScript 從來就沒有保證這么一說,前 99 個(gè)對(duì)象保持著形狀,可能第 100 個(gè)就少了某個(gè)屬性。

正是由于這樣的情況,所以編譯代碼需要在運(yùn)行之前檢查其假設(shè)是不是合理的。如果合理,那么優(yōu)化的編譯代碼會(huì)運(yùn)行,如果不合理,那么 JIT 會(huì)認(rèn)為做了一個(gè)錯(cuò)誤的假設(shè),并且把優(yōu)化代碼丟掉。

這時(shí)(發(fā)生優(yōu)化代碼丟棄的情況)執(zhí)行過程將會(huì)回到解釋器或者基線編譯器,這一過程叫做去優(yōu)化

通常優(yōu)化編譯器會(huì)使得代碼變得更快,但是一些情況也會(huì)引起一些意想不到的性能問題。如果你的代碼一直陷入優(yōu)化<->去優(yōu)化的怪圈,那么程序執(zhí)行將會(huì)變慢,還不如基線編譯器快。

大多數(shù)的瀏覽器都做了限制,當(dāng)優(yōu)化/去優(yōu)化循環(huán)發(fā)生的時(shí)候會(huì)嘗試跳出這種循環(huán)。比如,如果 JIT 做了 10 次以上的優(yōu)化并且又丟棄的操作,那么就不繼續(xù)嘗試去優(yōu)化這段代碼了樁。

一個(gè)優(yōu)化的例子:類型特化(Type specialization)

有很多不同類型的優(yōu)化方法,這里我介紹一種,讓大家能夠明白是如何優(yōu)化的。優(yōu)化編譯器最成功一個(gè)特點(diǎn)叫做類型特化,下面詳細(xì)解釋。

JavaScript 所使用的動(dòng)態(tài)類型體系在運(yùn)行時(shí)需要進(jìn)行額外的解釋工作,例如下面代碼:

function arraySum(arr) {
  var sum = 0;
  for (var i = 0; i < arr.length; i++) {
    sum += arr[i];
  }
}

+= 循環(huán)中這一步看起來很簡(jiǎn)單,只需要進(jìn)行一步計(jì)算,但是恰恰因?yàn)槭怯脛?dòng)態(tài)類型,他所需要的步驟要比你所想象的更復(fù)雜一些。

我們假設(shè) arr 是一個(gè)有 100 個(gè)整數(shù)的數(shù)組。當(dāng)代碼被標(biāo)記為 “warm” 時(shí),基線編譯器就為函數(shù)中的每一個(gè)操作生成一個(gè)樁。sum += arr[i] 會(huì)有一個(gè)相應(yīng)的樁,并且把里面的 += 操作當(dāng)成整數(shù)加法。

但是,sumarr[i] 兩個(gè)數(shù)并不保證都是整數(shù)。因?yàn)樵?JavaScript 中類型都是動(dòng)態(tài)類型,在接下來的循環(huán)當(dāng)中,arr[i] 很有可能變成了 string 類型。整數(shù)加法和字符串連接是完全不同的兩個(gè)操作,會(huì)被編譯成不同的機(jī)器碼。

JIT 處理這個(gè)問題的方法是編譯多基線樁。如果一個(gè)代碼段是單一形態(tài)的(即總是以同一類型被調(diào)用),則只生成一個(gè)樁。如果是多形態(tài)的(即調(diào)用的過程中,類型不斷變化),則會(huì)為操作所調(diào)用的每一個(gè)類型組合生成一個(gè)樁。

這就是說 JIT 在選擇一個(gè)樁之前,會(huì)進(jìn)行多分枝選擇,類似于決策樹,問自己很多問題才會(huì)確定最終選擇哪個(gè),見下圖:

正是因?yàn)樵诨€編譯器中每行代碼都有自己的樁,所以 JIT 在每行代碼被執(zhí)行的時(shí)候都會(huì)檢查數(shù)據(jù)類型。在循環(huán)的每次迭代,JIT 也都會(huì)重復(fù)一次分枝選擇。

如果代碼在執(zhí)行的過程中,JIT 不是每次都重復(fù)檢查的話,那么執(zhí)行的還會(huì)更快一些,而這就是優(yōu)化編譯器所需要做的工作之一了。

優(yōu)化編譯器中,整個(gè)函數(shù)被統(tǒng)一編譯,這樣的話就可以在循環(huán)開始執(zhí)行之前進(jìn)行類型檢查。

一些瀏覽器的 JIT 優(yōu)化更加復(fù)雜。比如在 Firefox 中,給一些數(shù)組設(shè)定了特定的類型,比如里面只包含整型。如果 arr 是這種數(shù)組類型,那么 JIT 就不需要檢查 arr[i] 是不是整型了,這也意味著 JIT 可以在進(jìn)入循環(huán)之前進(jìn)行所有的類型檢查。

總結(jié)

簡(jiǎn)而言之 JIT 是什么呢?它是使 JavaScript 運(yùn)行更快的一種手段,通過監(jiān)視代碼的運(yùn)行狀態(tài),把 hot 代碼(重復(fù)執(zhí)行多次的代碼)進(jìn)行優(yōu)化。通過這種方式,可以使 JavaScript 應(yīng)用的性能提升很多倍。

為了使執(zhí)行速度變快,JIT 會(huì)增加很多多余的開銷,這些開銷包括:

優(yōu)化和去優(yōu)化開銷

監(jiān)視器記錄信息對(duì)內(nèi)存的開銷

發(fā)生去優(yōu)化情況時(shí)恢復(fù)信息的記錄對(duì)內(nèi)存的開銷

對(duì)基線版本和優(yōu)化后版本記錄的內(nèi)存開銷

這里還有很大的提升空間:即消除開銷。通過消除開銷使得性能上有進(jìn)一步地提升,這也是 WebAssembly 所要做的事之一。

我最近正在寫一本《React.js 小書》,對(duì) React.js 感興趣的童鞋,歡迎指點(diǎn)。

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

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

相關(guān)文章

  • 通過動(dòng)圖形象為你介紹 flexbox 如何工作(一)

    摘要:為了完成這一目標(biāo),它允許容器自己來決定如何均勻地分布其中的元素包括他們的尺寸和他們之間的間距。 作者:Scott Domes 編譯:胡子大哈 翻譯原文:http://huziketang.com/blog/posts/detail?postId=58aaadb2fc5b7f63e8c23f69 英文原文:How Flexbox works?—?explained with big, ...

    everfly 評(píng)論0 收藏0
  • 通過動(dòng)圖形象為你介紹 Flexbox 如何工作

    摘要:解釋器的利弊解釋器啟動(dòng)和執(zhí)行的更快。正是因?yàn)檫@個(gè)原因,解釋器看起來更加適合。這就是為什么最開始的瀏覽器都是用解釋器的原因??墒钱?dāng)你運(yùn)行同樣的代碼一次以上的時(shí)候,解釋器的弊處就顯現(xiàn)出來了。起初,監(jiān)視器監(jiān)視著所有通過解釋器的代碼。 作者:Lin Clark 編譯:胡子大哈 翻譯原文:http://huziketang.com/blog/posts/detail?postId=58c12f...

    Shonim 評(píng)論0 收藏0
  • 拒絕爛大街——Flexbox布局演示站了解一下

    摘要:前言最近學(xué)到布局,當(dāng)時(shí)就感覺真是一個(gè)布局的神器。友好的提示初學(xué)者不易記全所有的屬性和值的左右,鼠標(biāo)放在屬性名上即可顯示相應(yīng)屬性的作用。 前言 最近學(xué)到Flexbox布局,當(dāng)時(shí)就感覺真是一個(gè)布局的神器。終于擺脫了利用各種定位浮動(dòng)布局的日子。想寫文章總結(jié)一下吧,發(fā)現(xiàn)介紹Flexbox布局類的文章都被寫爛了,各種大神寫的也是生動(dòng)形象,單單寫一個(gè)布局用法感覺除了自己記錄方便本地查并沒有什么意義...

    bigdevil_s 評(píng)論0 收藏0
  • Flexbox入門教程

    摘要:我們會(huì)在本文給出一個(gè)易于理解的入門介紹。項(xiàng)的順序的屬性另外一個(gè)的能力,是能夠輕松改變?cè)氐娘@示順序。她想讓成為頁(yè)面的第一個(gè)元素,顯示在之前??山邮艿闹涤?,或者一個(gè)數(shù)字后面緊跟著,,或其他長(zhǎng)度單位。 近幾年,CSS領(lǐng)域出現(xiàn)了一些復(fù)雜的專用布局工具,用以代替原有的諸如使用表格、浮動(dòng)和絕對(duì)定位之類的各種變通方案。Flexbox,或者說是彈性盒子布局模塊(Flexible Box Layout...

    劉永祥 評(píng)論0 收藏0

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

0條評(píng)論

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