1. 程式人生 > >WebAssembly入門,未來可期

WebAssembly入門,未來可期

先來幾個名詞

直譯器

一行行地邊解釋邊執行

此處輸入圖片的描述

編譯器

是把原始碼整個編譯成目的碼,執行時不再需要編譯器,直接在支援目的碼的平臺上執行。

此處輸入圖片的描述

直譯器的利弊

  • 直譯器啟動和執行的更快。你不需要等待整個編譯過程完成就可以執行你的程式碼。從第一行開始翻譯,就可以依次繼續執行了。
    正是因為這個原因,直譯器看起來更加適合 JavaScript。對於一個 Web 開發人員來講,能夠快速執行程式碼並看到結果是非常重要的。
    這就是為什麼最開始的瀏覽器都是用 JavaScript 直譯器的原因。

  • 可是當你運行同樣的程式碼一次以上的時候,直譯器的弊處就顯現出來了。比如你執行一個迴圈,那直譯器就不得不一次又一次的進行翻譯,這是一種效率低下的表現。

編譯器的利弊

  • 編譯器的問題則恰好相反。
    它需要花一些時間對整個原始碼進行編譯,然後生成目標檔案才能在機器上執行。對於有迴圈的程式碼執行的很快,因為它不需要重複的去翻譯每一次迴圈。

Just-in-time 及時編譯(簡稱JIT)

為了解決直譯器的低效問題,後來的瀏覽器把編譯器也引入進來,形成混合模式。

不同的瀏覽器實現這一功能的方式不同,不過其基本思想是一致的。在 JavaScript 引擎中增加一個監視器(也叫分析器)。監視器監控著程式碼的執行情況,記錄程式碼一共運行了多少次、如何執行的等資訊。

起初,監視器監視著所有通過直譯器的程式碼。

如果同一行程式碼運行了幾次,這個程式碼段就被標記成了 “warm”,如果運行了很多次,則被標記成 “hot”。

基線編譯器

如果一段程式碼變成了 “warm”,那麼 JIT 就把它送到編譯器去編譯,並且把編譯結果儲存起來。如果監視器監視到了執行同樣的程式碼和同樣的變數型別,那麼就直接把這個已編譯的版本 push 出來給瀏覽器。

優化編譯器

如果一個程式碼段變得 “very hot”,監視器會把它傳送到優化編譯器中。生成一個更快速和高效的程式碼版本出來,並且儲存之。
更多工作原理介紹請移步 JIT工作原理

JavaScript效能變化曲線

此處輸入圖片的描述

JIT不侷限於JS?

JIT總結

簡而言之 JIT 是什麼呢?它是使 JavaScript 執行更快的一種手段(JIT,內聯快取和隱藏類)之一,通過監視程式碼的執行狀態,把 hot 程式碼(重複執行多次的程式碼)進行優化。通過這種方式,可以使 JavaScript 應用的效能提升很多倍。

為了使執行速度變快,JIT 會增加很多多餘的開銷,這些開銷包括:

  • 優化和去優化開銷
  • 監視器記錄資訊對記憶體的開銷
  • 發生去優化情況時恢復資訊的記錄對記憶體的開銷

各大廠商的方案

  • 微軟的 TypeScript 通過為 JS 加入靜態型別檢查來改進 JS 鬆散的語法,提升程式碼健壯性;
  • 谷歌的 Dart 則是為瀏覽器引入新的虛擬機器去直接執行 Dart 程式以提升效能;
  • 火狐的 asm.js 則是取 JS 的子集,JS 引擎針對 asm.js 做效能優化。

以上嘗試各有優缺點

  • TypeScript 只是解決了 JS 語法鬆散的問題,最後還是需要編譯成 JS 去執行,對效能沒有提升;
  • Dart 只能在 Chrome 預覽版中執行,無主流瀏覽器支援,用 Dart 開發的人不多;

三大瀏覽器巨頭分別提出了自己的解決方案,互不相容,這違背了 Web 的宗旨; 是技術的規範統一讓 Web走到了今天,因此形成一套新的規範去解決 JS 所面臨的問題迫在眉睫。
於是 WebAssembly 誕生了

WebAssembly(Google , Microsoft , Mozilla , Apple 等幾家大公司合作發起的)

WebAssembly 是一種新的位元組碼格式,主流瀏覽器都已經支援 WebAssembly。 和 JS 需要解釋執行不同的是,WebAssembly 位元組碼和底層機器碼很相似可快速裝載執行,因此效能相對於 JS 解釋執行大大提升。 也就是說 WebAssembly 並不是一門程式語言,而是一份位元組碼標準,需要用高階程式語言編譯出位元組碼放到 WebAssembly 虛擬機器中才能執行.

WebAssembly優點

  • 體積小:由於瀏覽器執行時只加載編譯成的位元組碼,一樣的邏輯比用字串描述的 JS 檔案體積要小很多;
  • 載入快:由於檔案體積小,再加上無需解釋執行,WebAssembly 能更快的載入並例項化,減少執行前的等待時間;

能編譯成 WebAssembly 位元組碼的高階語言有:

  • AssemblyScript:語法和 TypeScript 一致,對前端來說學習成本低。
  • c\c++:官方推薦的方式
  • Rust:語法複雜、學習成本高,對前端來說可能會不適應。
  • Kotlin:語法和 Java、JS 相似,語言學習成本低。

WebAssembly是如何工作的

使用高階語言來編寫WebAssembly模組,將其編譯成.wasm檔案。這些.wasm檔案並不能直接被瀏覽器識別,所以它們需要一種稱為JavaScript膠接程式碼(glue code,用於連線相互不相容的元件,詳見:http://whatis.techtarget.com/definition/glue-code)的東西來載入。

此處輸入圖片的描述

1.Emscripten編譯器

C到WebAssembly的編譯器,推薦使用Emscripten(https://kripken.github.io/emscripten-site/docs/getting_started/downloads.html),安裝這個工具費時費力費空間。

有一個線上 C/C++ 轉 wasm 的工具: WasmExplorer

JavaScript膠接代

function loadWebAssembly (path) {
  return fetch(path)                   // 載入檔案        
    .then(res => res.arrayBuffer())    // 轉成 ArrayBuffer
    .then(WebAssembly.instantiate)     // 編譯 + 例項化
    .then(mod => mod.instance)         // 提取生成都模組
}

其實就是 【載入檔案】->【轉成 buffer】->【編譯】->【例項化】

DEMO C語言到WebAssembly

此處輸入圖片的描述

math.c

int square (int x) {
  return x * x;
}

loader.js

見上節JavaScript膠接代

相容性

各大主流瀏覽器都已經支援!
此處輸入圖片的描述

案例

  • 在2017年5月時,白鷺引擎宣佈開始支援 WebAssembly,而利用 WebAssembly,白鷺引擎可以將 HTML 5 程式碼編譯為機器碼執行,讓遊戲執行效能提升 300%。
  • 2017年10月底,谷歌開始支援讓 Google Earth 在 Firefox 上執行,其中的關鍵就是使用了 WebAssembly。
  • TeaVM 是一個 AOT 編譯器(翻譯器),可將 Java 位元組碼翻譯成 JavaScript 或 WebAssembly 格式。
  • Blazor — 讓 .NET 程式碼也能在瀏覽器執行
    開源 UI 框架 Blazor 可以讓 .NET 程式碼在瀏覽器環境中執行,而習慣 ASP.NET Razor 語法的開發者,仍可以繼續沿用習慣的開發模式

WebAssembly也許是JS效能的第二個拐點,未來可期

此處輸入圖片的描述

既然編譯好後的機器碼執行速度很快,為啥瀏覽器不嘗試直接支援編譯型語言??

  • JavaScript的歷史地位,WebAssembly與其相輔相成.JavaScript巨大的生態系統和友好的語法,WebAssembly接近原生的表現效能。
  • WebAssembly 位元組碼非常接近機器碼,可以非常快的被翻譯為對應架構的機器碼,因此 WebAssembly 執行速度和機器碼接近。
  • 類比Java 位元組碼