日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

WebAssembly 系列(五)为什么 WebAssembly 更快?

發(fā)布時(shí)間:2025/5/22 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 WebAssembly 系列(五)为什么 WebAssembly 更快? 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

作者:Lin Clark

編譯:胡子大哈

翻譯原文:huziketang.com/blog/posts/…

英文原文:What makes WebAssembly fast?

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


本文作者:Lin Clark
英文原文:What makes WebAssembly fast?

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

上一篇文章中,我介紹了如何編寫 WebAssembly 程序,也表達(dá)了我希望看到更多的開發(fā)者在自己的工程中同時(shí)使用 WebAssembly 和 JavaScript 的期許。

開發(fā)者們不必糾結(jié)于到底選擇 WebAssembly 還是 JavaScript,已經(jīng)有了 JavaScript 工程的開發(fā)者們,希望能把部分 JavaScript 替換成 WebAssembly 來嘗試使用。

例如,正在開發(fā) React 程序的團(tuán)隊(duì)可以把調(diào)節(jié)器代碼(即虛擬 DOM)替換成 WebAssembly 的版本。而對于你的 web 應(yīng)用的用戶來說,他們就跟以前一樣使用,不會發(fā)生任何變化,同時(shí)他們還能享受到 WebAssembly 所帶來的好處——快。

而開發(fā)者們選擇替換為 WebAssembly 的原因正是因?yàn)?WebAssembly 比較快。那么為什么它執(zhí)行的快呢?我們來一起了解一下。

當(dāng)前的 JavaScript 性能如何?

在我們了解 JavaScript 和 WebAssembly 的性能區(qū)別之前,需要先理解 JS 引擎的工作原理。

下面這張圖片介紹了性能使用的大概分布情況。

JS 引擎在圖中各個(gè)部分所花的時(shí)間取決于頁面所用的 JavaScript 代碼。圖表中的比例并不代表真實(shí)情況下的確切比例情況。

圖中的每一個(gè)顏色條都代表了不同的任務(wù):

  • Parsing——表示把源代碼變成解釋器可以運(yùn)行的代碼所花的時(shí)間;
  • Compiling + optimizing——表示基線編譯器和優(yōu)化編譯器花的時(shí)間。一些優(yōu)化編譯器的工作并不在主線程運(yùn)行,不包含在這里。
  • Re-optimizing——當(dāng) JIT 發(fā)現(xiàn)優(yōu)化假設(shè)錯(cuò)誤,丟棄優(yōu)化代碼所花的時(shí)間。包括重優(yōu)化的時(shí)間、拋棄并返回到基線編譯器的時(shí)間。
  • Execution——執(zhí)行代碼的時(shí)間
  • Garbage collection——垃圾回收,清理內(nèi)存的時(shí)間

這里注意:這些任務(wù)并不是離散執(zhí)行的,或者按固定順序依次執(zhí)行的。而是交叉執(zhí)行,比如正在進(jìn)行解析過程時(shí),其他一些代碼正在運(yùn)行,而另一些正在編譯。

這樣的交叉執(zhí)行給早期 JavaScript 帶來了很大的效率提升,早期的 JavaScript 執(zhí)行類似于下圖,各個(gè)過程順序進(jìn)行:

早期時(shí),JavaScript 只有解釋器,執(zhí)行起來非常慢。當(dāng)引入了 JIT 后,大大提升了執(zhí)行效率,縮短了執(zhí)行時(shí)間。

JIT 所付出的開銷是對代碼的監(jiān)視和編譯時(shí)間。JavaScript 開發(fā)者可以像以前那樣開發(fā) JavaScript 程序,而同樣的程序,解析和編譯的時(shí)間也大大縮短。這就使得開發(fā)者們更加傾向于開發(fā)更復(fù)雜的 JavaScript 應(yīng)用。

同時(shí),這也說明了執(zhí)行效率上還有很大的提升空間。

WebAssembly 對比

下面是 WebAssembly 和典型的 web 應(yīng)用的近似對比圖:

各種瀏覽器處理上圖中不同的過程,有著細(xì)微的差別,我用 SpiderMonkey 作為模型來講解不同的階段:

文件獲取

這一步并沒有顯示在圖表中,但是這看似簡單地從服務(wù)器獲取文件這個(gè)步驟,卻會花費(fèi)很長時(shí)間。

WebAssembly 比 JavaScript 的壓縮率更高,所以文件獲取也更快。即便通過壓縮算法可以顯著地減小 JavaScript 的包大小,但是壓縮后的 WebAssembly 的二進(jìn)制代碼依然更小。

這就是說在服務(wù)器和客戶端之間傳輸文件更快,尤其在網(wǎng)絡(luò)不好的情況下。

解析

當(dāng)?shù)竭_(dá)瀏覽器時(shí),JavaScript 源代碼就被解析成了抽象語法樹。

瀏覽器采用懶加載的方式進(jìn)行,只解析真正需要的部分,而對于瀏覽器暫時(shí)不需要的函數(shù)只保留它的樁。

解析過后 AST (抽象語法樹)就變成了中間代碼(叫做字節(jié)碼),提供給 JS 引擎編譯。

而 WebAssembly 則不需要這種轉(zhuǎn)換,因?yàn)樗旧砭褪侵虚g代碼。它要做的只是解碼并且檢查確認(rèn)代碼沒有錯(cuò)誤就可以了。

編譯和優(yōu)化

上一篇關(guān)于 JIT 的文章中,我有介紹過,JavaScript 是在代碼的執(zhí)行階段編譯的。因?yàn)樗侨躅愋驼Z言,當(dāng)變量類型發(fā)生變化時(shí),同樣的代碼會被編譯成不同版本。

不同瀏覽器處理 WebAssembly 的編譯過程也不同,有些瀏覽器只對 WebAssembly 做基線編譯,而另一些瀏覽器用 JIT 來編譯。

不論哪種方式,WebAssembly 都更貼近機(jī)器碼,所以它更快,使它更快的原因有幾個(gè):

  • 在編譯優(yōu)化代碼之前,它不需要提前運(yùn)行代碼以知道變量都是什么類型。
  • 編譯器不需要對同樣的代碼做不同版本的編譯。
  • 很多優(yōu)化在 LLVM 階段就已經(jīng)做完了,所以在編譯和優(yōu)化的時(shí)候沒有太多的優(yōu)化需要做。
  • 重優(yōu)化

    有些情況下,JIT 會反復(fù)地進(jìn)行“拋棄優(yōu)化代碼<->重優(yōu)化”過程。

    當(dāng) JIT 在優(yōu)化假設(shè)階段做的假設(shè),執(zhí)行階段發(fā)現(xiàn)是不正確的時(shí)候,就會發(fā)生這種情況。比如當(dāng)循環(huán)中發(fā)現(xiàn)本次循環(huán)所使用的變量類型和上次循環(huán)的類型不一樣,或者原型鏈中插入了新的函數(shù),都會使 JIT 拋棄已優(yōu)化的代碼。

    反優(yōu)化過程有兩部分開銷。第一,需要花時(shí)間丟掉已優(yōu)化的代碼并且回到基線版本。第二,如果函數(shù)依舊頻繁被調(diào)用,JIT 可能會再次把它發(fā)送到優(yōu)化編譯器,又做一次優(yōu)化編譯,這是在做無用功。

    在 WebAssembly 中,類型都是確定了的,所以 JIT 不需要根據(jù)變量的類型做優(yōu)化假設(shè)。也就是說 WebAssembly 沒有重優(yōu)化階段。

    執(zhí)行

    自己也可以寫出執(zhí)行效率很高的 JavaScript 代碼。你需要了解 JIT 的優(yōu)化機(jī)制,例如你要知道什么樣的代碼編譯器會對其進(jìn)行特殊處理(JIT 文章里面有提到過)。

    然而大多數(shù)的開發(fā)者是不知道 JIT 內(nèi)部的實(shí)現(xiàn)機(jī)制的。即使開發(fā)者知道 JIT 的內(nèi)部機(jī)制,也很難寫出符合 JIT 標(biāo)準(zhǔn)的代碼,因?yàn)槿藗兺ǔ榱舜a可讀性更好而使用的編碼模式,恰恰不合適編譯器對代碼的優(yōu)化。

    加之 JIT 會針對不同的瀏覽器做不同的優(yōu)化,所以對于一個(gè)瀏覽器優(yōu)化的比較好,很可能在另外一個(gè)瀏覽器上執(zhí)行效率就比較差。

    正是因?yàn)檫@樣,執(zhí)行 WebAssembly 通常會比較快,很多 JIT 為 JavaScript 所做的優(yōu)化在 WebAssembly 并不需要。另外,WebAssembly 就是為了編譯器而設(shè)計(jì)的,開發(fā)人員不直接對其進(jìn)行編程,這樣就使得 WebAssembly 專注于提供更加理想的指令(執(zhí)行效率更高的指令)給機(jī)器就好了。

    執(zhí)行效率方面,不同的代碼功能有不同的效果,一般來講執(zhí)行效率會提高 10% - 800%。

    垃圾回收

    JavaScript 中,開發(fā)者不需要手動清理內(nèi)存中不用的變量。JS 引擎會自動地做這件事情,這個(gè)過程叫做垃圾回收。

    可是,當(dāng)你想要實(shí)現(xiàn)性能可控,垃圾回收可能就是個(gè)問題了。垃圾回收器會自動開始,這是不受你控制的,所以很有可能它會在一個(gè)不合適的時(shí)機(jī)啟動。目前的大多數(shù)瀏覽器已經(jīng)能給垃圾回收安排一個(gè)合理的啟動時(shí)間,不過這還是會增加代碼執(zhí)行的開銷。

    目前為止,WebAssembly 不支持垃圾回收。內(nèi)存操作都是手動控制的(像 C、C++一樣)。這對于開發(fā)者來講確實(shí)增加了些開發(fā)成本,不過這也使代碼的執(zhí)行效率更高。

    總結(jié)

    WebAssembly 比 JavaScript 執(zhí)行更快是因?yàn)?#xff1a;

    • 文件抓取階段,WebAssembly 比 JavaScript 抓取文件更快。即使 JavaScript 進(jìn)行了壓縮,WebAssembly 文件的體積也比 JavaScript 更小;
    • 解析階段,WebAssembly 的解碼時(shí)間比 JavaScript 的解析時(shí)間更短;
    • 編譯和優(yōu)化階段,WebAssembly 更具優(yōu)勢,因?yàn)?WebAssembly 的代碼更接近機(jī)器碼,而 JavaScript 要先通過服務(wù)器端進(jìn)行代碼優(yōu)化。
    • 重優(yōu)化階段,WebAssembly 不會發(fā)生重優(yōu)化現(xiàn)象。而 JS 引擎的優(yōu)化假設(shè)則可能會發(fā)生“拋棄優(yōu)化代碼<->重優(yōu)化”現(xiàn)象。
    • 執(zhí)行階段,WebAssembly 更快是因?yàn)殚_發(fā)人員不需要懂太多的編譯器技巧,而這在 JavaScript 中是需要的。WebAssembly 代碼也更適合生成機(jī)器執(zhí)行效率更高的指令。
    • 垃圾回收階段,WebAssembly 垃圾回收都是手動控制的,效率比自動回收更高。

    這就是為什么在大多數(shù)情況下,同一個(gè)任務(wù) WebAssembly 比 JavaScript 表現(xiàn)更好的原因。

    但是,還有一些情況 WebAssembly 表現(xiàn)的會不如預(yù)期;同時(shí) WebAssembly 的未來也會朝著使 WebAssembly 執(zhí)行效率更高的方向發(fā)展。這些我會在下一篇文章《WebAssembly 系列(六)WebAssembly 的現(xiàn)在與未來》中介紹。


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

    《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀

    總結(jié)

    以上是生活随笔為你收集整理的WebAssembly 系列(五)为什么 WebAssembly 更快?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。