摘要:本文是圖說系列文章的第五篇。這樣的話,使用的開發者也不需要做任何適配,但是它們卻能獲得更高性能。該圖并不是用來準確的衡量其性能的。運行編寫出高性能的代碼是可能的。這種清理工作由引擎自動進行,稱為垃圾回收。
本文是圖說 WebAssembly 系列文章的第五篇。如果您還未閱讀之前的文章,建議您從第一篇入手。
在上一篇文章中,我們說到了使用 WebAssembly 和 JavaScript 并不是兩選一的選擇。我們并不希望太多開發者只使用 WebAssembly 。
我們希望開發者可以把部分 JavaScript 代碼替換為 WebAssembly 。
例如,React 團隊可以把虛擬 DOM 改用 WebAssembly 來實現。這樣的話,使用 React 的開發者也不需要做任何適配,但是它們卻能獲得更高性能。
能夠促使 React 團隊這么做的原因最可能是 WebAssembly 的高性能。但是到底是什么使它有高性能呢?
JS 性能分析在我們理解 JavaScript 和 WebAssembly 之間的性能差異原因之前,我們需要先理解 JavaScript 引擎所做的工作。
下圖給了一個粗糙的描述,概括了當前 JS 應用的啟動性能。
JS 引擎在這些任務上所耗費的時間取決于頁面所用的 JS 代碼。該圖并不是用來準確的衡量其性能的。相反,它是一種高度抽象的模型,用來比較實現相同功能的 JavaScript 和 WebAssembly 之間的性能差異。
圖中的每一塊表示該任務所耗費的時間。
解析:把 JavaScript 源碼解析為解釋器能夠運行的代碼的時間。
編譯+優化:基準編譯器和優化編譯器所耗費的時間。優化編譯器的部分優化工作并不是在主線程上進行的,這部分耗費的時間不包含在這里。
重新優化:當假設不成立時,JIT 作出重新調整所耗費的時間,包括重新優化和回退到基準代碼的時間。
執行:運行代碼耗費的時間。
垃圾回收:清理內存耗費的時間。
要注意的是,這些過程并不會以離散塊或者特定的順序發生。相反,它們是交叉進行的。
可能會解析完一小段,就會運行一段,然后編譯一段;接著解析更多代碼,然后執行更多代碼等等。
這種分段交叉進行的設計相比早期的 JavaScript 來說是一種很大的性能提升,早期的 JavaScript 執行更像是下圖中的情形。
在最開始的時候,只有解析器來跑 JavaScript ,執行速度是相當慢的。當引入 JIT 后,執行速度得到了大幅提升。
當然,引入 JIT 的代價就是在監視器和編譯器上投入了更多資源。
如果開發者還是按照以前的方式來編寫 JavaScript 應用,那么其實解析和編譯時間是很小的。
只不過隨著性能提升,開發者開發出了更大型的 JavaScript 應用,對性能要求又變高了。
因此,性能還是有提升空間的。
WebAssembly 性能分析下圖是與典型網頁應用相比時,WebAssembly 的大致過程。
不同瀏覽器的處理可能略有不同,下面我們以 SpiderMonkey 引擎為例來說明各個過程。
加載加載這部分并沒有體現在上圖中,但這部分所耗費的時間就是從服務器下載文件的時間。
因為 WebAssembly 代碼比 JavaScript 代碼更加的精簡,所以加載 WebAssembly 文件是更快的。
盡管壓縮算法能夠極大減小 JavaScript 代碼的體積,但是 WebAssembly 壓縮后的二進制代碼仍然比它要小。
這就意味著下載將耗費更少時間,尤其是在低網速情況下。
解析JavaScript 代碼一旦下載到瀏覽器,它會被解析為抽象語法樹(AST)。
瀏覽器通常采用的策略是惰性處理,即只解析真正被用到的代碼以及只為還沒被調用的函數創建存根。
之后,AST 被轉化為引擎相關的中間代碼:字節碼(Bytecode)。
而 WebAssembly 則不需要這種轉換,因為它本身已經是一種中間代碼了。它只需要經過解碼,并且驗證解碼沒有發生錯誤即可。
編譯+優化正如前面關于 JIT 的文章所說,JavaScript 的編譯時發生在代碼運行期間的。根據運行時所用的不同數據類型,相同代碼可能需要被編譯為多種代碼。
不同的瀏覽器編譯 WebAssembly 時使用不同方式。一些瀏覽器會在運行代碼前先進行基準編譯,其他瀏覽器則會使用 JIT 。
但不管是哪種方式,WebAssembly 都是從里機器碼比較近的地方開始的。比如說,程序本身就包含了數據的類型信息,這樣的話就會有更高的性能,因為:
編譯器再進行優化編譯之前不需要耗費時間來檢查數據類型
編譯器并不需要基于不同類型來編譯出相同代碼的不同類型版本代碼
有很多優化已經在 LLVM 之前完成了,所以這里可以減少編譯和優化的開銷
重新優化有時候 JIT 必須丟棄之前已經優化的代碼并且重新編譯。
這種情況就發生在 JIT 之前的假設都不成立時。比如說,當循環中使用了與之前不一樣的變量類型,或者原型鏈上新增了一個函數。
這種去優化帶來了兩個性能損耗。一是,JIT 需要丟棄已編譯的代碼并回退到基準代碼;二是,如果這段代碼仍然會被調用很多次,那么又得重新花費時間去再次優化它。
而在 WebAssembly 中,數據類型是很明確的,所以 JIT 不需要對運行時的數據類型做任何假設。也就意味著,它不不存在重新優化可能。
運行編寫出高性能的 JavaScript 代碼是可能的。為此,你需要知道 JIT 是如何做優化的。
比如,你需要知道如何寫出讓編譯器特定化數據類型的代碼。
但是,大多數開發者并不知道 JIT 的內部實現。即便是了解 JIT 內部實現的人,也很難直接擊中要害。
許多我們為了讓代碼更具可讀性的編程模式(比如抽出公共函數來處理多種類型)反而阻礙了編譯器的優化。
而且,不同瀏覽器的 JIT 所采用的各種優化手段是不同的,這就導致了可能在某款瀏覽器上是最優的,但是在另一款瀏覽器中則是很差的。
正因為這個,運行 WebAssembly 通常是更加快速的。許多 JIT 做的優化在 WebAssembly 中根本不存在。
此外,WebAssembly 是被設計為一個編譯目標的。也就是說,它是被用來作為編譯器輸出的,而不是用來供開發者編碼的。
因為開發者不需要直接對 WebAssembly 編碼,所以它能夠使用更適合機器的指令,而這些指令通常能做到 10% ~ 800% 的性能提升。
垃圾回收在 JavaScript 中,開發者并不需要專門去清理那些不再使用的變量所占用的內存。這種清理工作由 JavaScript 引擎自動進行,稱為垃圾回收(Garbage Collection)。
但是,如果你想要得到可預期的性能,這可能會成為阻礙。
你并不能控制什么時候進行垃圾回收,所以它隨時可能發生。盡管大多數瀏覽器在垃圾回收的調度方面做的相當不錯,但是它仍然可能阻礙你的代碼運行。
至少現在,WebAssembly 根本不支持垃圾回收。所有內存都是手動管理的。
雖然這樣會讓編碼變得更加困難,但是它也讓性能變得更加穩定。
所以,WebAssembly 之所以比 JavaScript 擁有更好的性能,是因為以下原因:
更精簡的體積讓加載 WebAssembly 耗費更少時間
解碼 WebAssembly 比解析 JavaScript 更快
編譯和優化節省了時間開銷,因為 WebAssembly 比 JavaScript 更接近機器碼,而且 WebAssembly 是已經提前做過優化的
不會發生重新優化的過程,因為 WebAssembly 自帶數據類型和其他信息
代碼的運行耗費更好時間,因為它沒有那么多編譯器陷阱,而且它的指令集對機器更友好
不存在垃圾回收的過程,因為它是手動管理內存的
以上就是為什么在大多數時候,WebAssembly 都比 JavaScript 性能好的原因。
當然,WebAssembly 也存在表現并不如期望的那樣好的時候。同時,也有一些正在進行的改變使得它變得更快。這些我們會在下一篇中討論。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/94758.html
摘要:性能簡史在年,被創造出來時并不是沖著性能去的。而且在之后的十年發展中,它的性能一直是很低的。的引入成就了性能提升的一個轉折點,其執行速度比以往快了之多。性能提升也使得在全新的問題上使用成為可能。現在,極可能是下一個性能轉折點。 你可能已經聽說 WebAssembly 代碼跑起來非常快。但是你知道這是為什么嗎?在本系列文章中,我們將探究其原因。 何為 WebAssembly WebAss...
摘要:現狀年月日,主流的四大瀏覽器達成了共識并宣布的最小可行產品已經完成。更快的函數調用當前,在中調用函數比想象的要慢。直接操作目前,沒有任何方式能夠操作。這就導致了部分應用可能會因此而推遲發布時間。結束現如今已經相當快速。 本文是圖說 WebAssembly 系列文章的最后一篇。如果您還未閱讀之前的文章,建議您從第一篇入手。 現狀 2017 年 2 月 28 日,主流的四大瀏覽器達成了共識...
摘要:編譯器優缺點與解釋器相比,編譯器有著相反的優缺點。它們為引擎新增了一個組件,稱為監視器,或者。優化編譯器會基于監視器記錄的代碼運行信息來作出一些判斷。通常來說,優化編譯器會使得代碼跑的更快。而這正是優化編譯器所做的優化之一。 本文是圖說 WebAssembly 系列文章的第二篇,如果你還沒閱讀其它的,建議您從第一篇開始。 JavaScript 的運行,一開始是很慢的,但是后面會變得越來...
摘要:為了更好的理解,我們有必要去先理解什么是匯編,以及編譯器是如何產生匯編的。什么是匯編現在,我們來看看外星人的大腦是如何工作的。這些注釋就是匯編,也稱為符號機器碼。結束以上的內容就是什么是匯編以及它是如何從高級編程語言翻譯過來的。 本文是圖說 WebAssembly 系列文章的第三篇。如果您還未閱讀之前的文章,建議您從第一篇入手。 為了更好的理解 WebAssembly ,我們有必要去先...
摘要:本文是圖說系列文章的第四篇。它們表示一種可以在普遍流行機器上高效使用的指令集合。這是因為是一種稱為堆棧機器。盡管是根據堆棧機器來設計的,但是這并不是它在真實物理機器上工作的方式。這些內容稱為段。 本文是圖說 WebAssembly 系列文章的第四篇。如果您還未閱讀之前的文章,建議您從第一篇入手。 WebAssembly 是一種使得除 JavaScript 以外的編程語言也能運行在網頁上...
閱讀 2950·2021-11-23 09:51
閱讀 3776·2021-11-22 15:29
閱讀 3226·2021-10-08 10:05
閱讀 1552·2021-09-22 15:20
閱讀 952·2019-08-30 15:56
閱讀 1069·2019-08-30 15:54
閱讀 732·2019-08-26 11:54
閱讀 2635·2019-08-26 11:32