WebAssembly 入門

引言

先看下 官網 給的定義。

WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable compilation target for programming languages, enabling deployment on the web for client and server applications.

WebAssembly 是基於棧式虛擬機的二進制指令集,可以作爲編程語言的編譯目標,能夠部署在 web 客戶端和服務端的應用中。

第一次看到這個定義的時候是一頭霧水,翻了一些資料漸漸有了點輪廓,下邊分享下我目前的理解。

首先 WebAssembly 是由 WebAssembly 兩個詞構成,其中 Web 表明它一定和前端有關。Assembly 的意思是彙編,彙編對應機器碼,而機器碼和 CPU 的指令集有關,接下來補一下相關的知識。

相關概念

其中指令集、操作系統相關的知識,之前總結過幾篇文章,到底學哪一門編程語言x86,x64,x86-64,amd64,arm 指令集架構之間的關係linux 和 Android 的關係,可以先過去看一下,這裏的話抽主要的部分回顧一下。

參考上圖,計算機的主要架構如上。最底層是 CPU 的指令集,主要分爲複雜指令集和簡單指令集。

複雜指令集是 x86x64(也叫 x86-64, amd64) 兩種架構,專利在 IntelAMD 兩家公司手裏, 該架構 CPU 主要是 IntelAMD 兩家公司,這種 CPU 常用在 PC 機上,包括 WindowsmacOSLinux

簡單指令集是 arm 一種架構,專利在 ARM 公司手裏,該架構 CPU 主要有高通、三星、蘋果、華爲海思、聯發科等公司。這種 CPU 常用在手機上,包括安卓和蘋果。

指令集是什麼呢?直接把阮一峯的老師的一個 例子 粘過來,大家可以看一下。

c 語言的源程序。

int add_a_and_b(int a, int b) {
   return a + b;
}

int main() {
   return add_a_and_b(2, 3);
}

所對應的彙編就是下邊的樣子。

_add_a_and_b:
   push   %ebx
   mov    %eax, [%esp+8] 
   mov    %ebx, [%esp+12]
   add    %eax, %ebx 
   pop    %ebx 
   ret  

_main:
   push   3
   push   2
   call   _add_a_and_b 
   add    %esp, 8
   ret

這裏的 pushmov 每一條指令就是指令集規定的內容,規定了操作碼、操作數以及具體的功能。當然這裏是用匯編表示的,主要是爲了我們人類來讀寫,最終還會轉成 0,1 序列。上邊每個單詞都會有一個數字相對應,比如 add 指令對應 00000011

通過規定的指令集(加法的指令,壓棧指令等),編寫相關程序,然後 CPU 就會一條一條的執行,最終實現相應的功能。

WebAssembly 就規定了一套指令集,更準確的來說是虛擬指令集,因爲這套指令集是跑在虛擬機上的,而不是直接由硬件運行。

歷史

上邊我們知道了 WebAssemblyAssembly ,即彙編,也就是指令集。下邊在回顧下 Web,即 WebAssembly誕生的原因。

這裏就得談到 javaScript 了,衆所周知, javaScript 是一門動態類型的語言,編寫程序時無需考慮變量類型,而且還可以運行時改變類型。對於我們開發者,確實很方便,但對於運行它的引擎就很有問題了。參考 這裏 的一張圖,看一下 V8 引擎從 js 源碼到執行的一個過程。

由於 js 的動態類型,解釋器在執行代碼的時候會在類型判斷上帶來一定的性能消耗,降低執行速度。所以 V8 引擎採用了 JIT(即時編譯技術) 技術,監控一些經常執行的代碼,將其編譯成 CPU 直接執行的機器碼,提高執行速度。但由於 js 動態類型,在某些情況下還得反優化,回到字節碼進行執行。

隨着前端的不斷髮展,項目的大小和複雜度不斷增大,對於某些場景,性能上可能已經無法滿足,瀏覽器廠商們也一直在探索性能優化的方法。

NaCl/PNaCl

2011GoogleChrome 中使用了 NaCl 技術,可以使得 C 語言編寫的程序運行到瀏覽器中,下邊是維基百科 的定義。

Google Native Client(縮寫爲 NaCl),是一個由谷歌所發起的開放源代碼計劃,採用 BSD 許可證。它採用沙盒技術,讓 Intel x86、ARM 或 MIPS 子集的機器代碼直接在沙盒上運行。它能夠從瀏覽器直接運行程序機器代碼,獨立於用戶的操作系統之外,使 Web 應用程序可以用接近於機器代碼運作的速度來運行,同時兼顧安全性。其功能類似於微軟的 ActiveX,但是 ActiveX 只支持視窗系統。

但一個完整的 NaCl 應用,在分發時需要提供支持多個架構平臺(X86 / X64 / ARM 等)的模塊文件,後來谷歌又推出了與底層架構無關的 PNaCl 技術。但由於其開發難度、兼容性等問題最終沒有普及開來。在 2017Google 宣佈放棄 PNaCl 轉向 WebAssembly

ASM.js

ASM.jsMozilla2013 年推出的,是 javaScript 的一個嚴格子集,可以作爲 C/C++ 編譯的目標語言,從而使得 js 引擎可以採用 AOT(Ahead Of Time) 的編譯策略,也就是在運行前直接編譯成機器碼,因此運行速度會有一定的提升。

ASM.js 通常不直接編寫,而是作爲一種通過編譯器生成的中間語言,該編譯器獲取 C++ 或其他語言的源代碼,然後輸出 ASM.js

例如下邊的 C 語言代碼。

int f(int i) {
  return i + 1;
}

經過編譯器編譯會生成下邊的 js 代碼。

function f(i) {
  i = i|0;
  return (i + 1)|0;
}

注意這裏的|0js 中相當於和 0 進行了或操作,所以不影響原本的邏輯。在 asm.js 中起到了類型標記的作用,這樣 js 引擎執行的時候就知道 i 是一個整型,返回值是一個整型。除了或操作這種,ASM.js 標準中還規定了很多類似的標記規則,用於告訴 js 引擎變量的類型,便於進行 AOT 優化。

這看起來和 TypeScript 很像,但其實不是一種東西。TypeScriptjs 的一個超集,瀏覽器並不能直接執行 ts,還需要轉換爲 js 去執行。ts 主要是幫助我們開發人員去看的,增加了代碼的可讀性,也可以讓編輯器提前發現一些錯誤。而 asm.js 是用於引擎的編譯優化。

WebAssembly

接下來看一下 WebAssembly 的歷史。

2015 年 4 月,WebAssembly Community Group 成立;

2015 年 6 月,WebAssembly 第一次以 WCG 的官方名義向外界公佈;

2016 年 8 月,WebAssembly 開始進入了漫長的 “Browser Preview” 階段;

2017 年 2 月,WebAssembly 官方 LOGO 在 Github 上的衆多討論中被最終確定;同年同月,一個歷史性的階段,四大瀏覽器(FireFox、Chrome、Edge、WebKit)在 WebAssembly 的 MVP(最小可用版本)標準實現上達成共識,這意味着 WebAssembly 在其 MVP 標準上的 “Brower Preview” 階段已經結束;

2017 年 8 月,W3C WebAssembly Working Group 成立,意味着 WebAssembly 正式成爲 W3C 衆多技術標準中的一員。

WebAssembly2019125 日成爲萬維網聯盟(W3C)的推薦標準,與 HTMLCSSJavaScript 一起成爲 Web 的第四種語言。

可以看一下目前瀏覽器的支持程度,已經算比較高了。

初體驗

內部結構

目前已經有了將 C/C++RusttsC#GoKotlinSwift 等語言轉換爲 WebAssembly(wasm) 的工具,下邊我們體驗一下 C++ 轉換的過程。

首先編寫一個 C++ 程序 fibonacci.cc,斐波納契數字的遞歸寫法。

#include <emscripten.h>
extern "C" {
  EMSCRIPTEN_KEEPALIVE 
  int fibonacci(int n) {
    if(n < 2) {
      return 1;
    }
    return fibonacci(n - 1) + fibonacci(n - 2);
  }
}

函數的定義置在 extern “C” {} 結構中,是爲了防止函數名編譯後被改變。EMSCRIPTEN_KEEPALIVE 是爲了確保函數不會在編譯器的編譯過程中,被 DCE(Dead Code 」limination)過程處理掉。

然後需要安裝 Emscripten 用來將 C++ 程序編譯爲 WebAssembly(wasm) 的程序,安裝後執行下邊的命令。

emcc fibonacci.cc -s WASM=1 -O3 --no-entry -o fibonacci.wasm

-s WASM=1 表明編譯成 Webassembly 的程序,-O3 表明編譯的優化程度,–no-entry 參數告訴編譯器沒有聲明 main 函數,-o 指定生成的文件名。

讓我們看一下生成的字節碼文件 fibonacci.wasm

  Offset: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F     
00000000: 00 61 73 6D 01 00 00 00 01 11 04 60 00 01 7F 60    .asm.......`...`
00000010: 01 7F 01 7F 60 00 00 60 01 7F 00 03 07 06 02 01    ....`..`........
00000020: 00 03 01 00 04 05 01 70 01 02 02 05 06 01 01 80    .......p........
00000030: 02 80 02 06 0F 02 7F 01 41 90 88 C0 02 0B 7F 00    ........A..@....
00000040: 41 84 08 0B 07 88 01 09 06 6D 65 6D 6F 72 79 02    A........memory.
00000050: 00 19 5F 5F 69 6E 64 69 72 65 63 74 5F 66 75 6E    ..__indirect_fun
00000060: 63 74 69 6F 6E 5F 74 61 62 6C 65 01 00 09 66 69    ction_table...fi
00000070: 62 6F 6E 61 63 63 69 00 01 0B 5F 69 6E 69 74 69    bonacci..._initi
00000080: 61 6C 69 7A 65 00 00 10 5F 5F 65 72 72 6E 6F 5F    alize...__errno_
00000090: 6C 6F 63 61 74 69 6F 6E 00 05 09 73 74 61 63 6B    location...stack
000000a0: 53 61 76 65 00 02 0C 73 74 61 63 6B 52 65 73 74    Save...stackRest
000000b0: 6F 72 65 00 03 0A 73 74 61 63 6B 41 6C 6C 6F 63    ore...stackAlloc
000000c0: 00 04 0A 5F 5F 64 61 74 61 5F 65 6E 64 03 01 09    ...__data_end...
000000d0: 07 01 00 41 01 0B 01 00 0A 66 06 03 00 01 0B 3D    ...A.....f.....=
000000e0: 01 02 7F 41 01 21 01 20 00 41 02 4E 04 7F 41 00    ...A.!...A.N..A.
000000f0: 21 01 03 40 20 00 41 7F 6A 10 01 20 01 6A 21 01    !..@..A.j....j!.
00000100: 20 00 41 03 4A 21 02 20 00 41 7E 6A 21 00 20 02    ..A.J!...A~j!...
00000110: 0D 00 0B 20 01 41 01 6A 05 41 01 0B 0B 04 00 23    .....A.j.A.....#
00000120: 00 0B 06 00 20 00 24 00 0B 10 00 23 00 20 00 6B    ......$....#...k
00000130: 41 70 71 22 00 24 00 20 00 0B 05 00 41 80 08 0B    Apq".$......A...

讓我們來解讀下,最開始的前八個字節 0x0 0x61 0x73 0x6d 0x1 0x0 0x0 0x0 表明當前是一個 wasm 的模塊。然後會分很多 SectionFunction SectionCode Section 等等,都有特定的數字對應,還有就是文章開頭講的指令操作符所對應的一些數字。

看着上邊的字節碼彷彿回到了上古時期直接用機器碼編程的時代,當年出現了彙編語言。這裏也會有類似彙編的東西,那就是 WAT(WebAssembly Text Format)

需要安裝 WABT , 然後執行 wasm2wat 命令。

../wabt/bin/wasm2wat fibonacci.wasm -o fibonacci.wat

然後就生成了 fibonacci.wat 文件。

(module
  (type (;0;) (func (result i32)))
  (type (;1;) (func (param i32) (result i32)))
  (type (;2;) (func))
  (type (;3;) (func (param i32)))
  (func (;0;) (type 2)
    nop)
  (func (;1;) (type 1) (param i32) (result i32)
    (local i32 i32)
    i32.const 1
    local.set 1
    local.get 0
    i32.const 2
    i32.ge_s
    if (result i32)  ;; label = @1
      i32.const 0
      local.set 1
      loop  ;; label = @2
        local.get 0
        i32.const -1
        i32.add
        call 1
        local.get 1
        i32.add
        local.set 1
        local.get 0
        i32.const 3
        i32.gt_s
        local.set 2
        local.get 0
        i32.const -2
        i32.add
        local.set 0
        local.get 2
        br_if 0 (;@2;)
      end
      local.get 1
      i32.const 1
      i32.add
    else
      i32.const 1
    end)
  (func (;2;) (type 0) (result i32)
    global.get 0)
  (func (;3;) (type 3) (param i32)
    local.get 0
    global.set 0)
  (func (;4;) (type 1) (param i32) (result i32)
    global.get 0
    local.get 0
    i32.sub
    i32.const -16
    i32.and
    local.tee 0
    global.set 0
    local.get 0)
  (func (;5;) (type 0) (result i32)
    i32.const 1024)
  (table (;0;) 2 2 funcref)
  (memory (;0;) 256 256)
  (global (;0;) (mut i32) (i32.const 5243920))
  (global (;1;) i32 (i32.const 1028))
  (export "memory" (memory 0))
  (export "__indirect_function_table" (table 0))
  (export "fibonacci" (func 1))
  (export "_initialize" (func 0))
  (export "__errno_location" (func 5))
  (export "stackSave" (func 2))
  (export "stackRestore" (func 3))
  (export "stackAlloc" (func 4))
  (export "__data_end" (global 1))
  (elem (;0;) (i32.const 1) func 0))

上邊的格式屬於 「S- 表達式」, Lisp 語言就是採用的這種表達式,每條語句都是先執行最裏邊括號的表達式然後依次展開。

使用方法

上邊主要介紹了 .wasm 具體長什麼樣子,下邊看一下怎麼用到瀏覽器中。

.wasm 源文件到實例化的對象主要有三個步驟,加載 -> 編譯 -> 實例化 -> 調用

加載:讀取 .wasm 字節碼到本地中,一般是通過 fetch 從網絡中取得。

編譯:在 Worker 線程進行,編譯成平臺相關的代碼。

實例化:將宿主環境的一些對象、方法導入到 wasm 模塊中,比如導入操作 dom 的方法。

調用:通過上一步已經實例化的對象,來調用 wasm 模塊中的方法。

主要有兩種類型的 API,一種是 js 提供的 api ,另一種是 Web 提供的 apiWeb 提供的 api 支持流式編譯實例化。

js 的方法,

WebAssembly.instantiate(bufferSource, importObject),可以完成編譯和實例化。

bufferSource 是含有效 Wasm 模塊二進制字節碼的 ArrayBufferTypedArray 對象。

importObject 是要導入到 Wasm 模塊中的對象。

方法在調用後返回一個Promise 對象,resolve 後返回一個對象,該對象包含編譯好的  module 和已經實例化的 instance,模塊導出的方法可以通過 instance 對象進行調用。

web 的方法,

WebAssembly.instantiateStreaming(source, importObject)

不同之處在於第一個參數,這裏的 source 指的是尚未 ResolveResponse 對象(window.fetch 調用後會返回該對象),好處就是可以邊讀取 .wasm 字節流,邊進行編譯。

其他參數和返回值和 jsapi 均一致。

js API 嘗試

先簡單的嘗試一下,我們直接構造一個 wasm 模塊的  TypedArray 對象,該模塊包含了一個 add 方法,然後調用 WebAssembly.instantiate 進行編譯和實例化。

對應的 C++ 代碼。

#include <emscripten.h>

extern "C" {
  EMSCRIPTEN_KEEPALIVE 
  int add(int a, int b) {
    return a + b;
  }
}

對應的 .wasm 字節碼。

00 61 73 6D 01 00 00 00 01 17 05 60 00 01 7F 60
00 00 60 01 7F 00 60 01 7F 01 7F 60 02 7F 7F 01
7F 03 07 06 01 04 00 02 03 00 04 05 01 70 01 02
02 05 06 01 01 80 02 80 02 06 0F 02 7F 01 41 90
88 C0 02 0B 7F 00 41 84 08 0B 07 82 01 09 06 6D
65 6D 6F 72 79 02 00 19 5F 5F 69 6E 64 69 72 65
63 74 5F 66 75 6E 63 74 69 6F 6E 5F 74 61 62 6C
65 01 00 03 61 64 64 00 01 0B 5F 69 6E 69 74 69
61 6C 69 7A 65 00 00 10 5F 5F 65 72 72 6E 6F 5F
6C 6F 63 61 74 69 6F 6E 00 05 09 73 74 61 63 6B
53 61 76 65 00 02 0C 73 74 61 63 6B 52 65 73 74
6F 72 65 00 03 0A 73 74 61 63 6B 41 6C 6C 6F 63
00 04 0A 5F 5F 64 61 74 61 5F 65 6E 64 03 01 09
07 01 00 41 01 0B 01 00 0A 30 06 03 00 01 0B 07
00 20 00 20 01 6A 0B 04 00 23 00 0B 06 00 20 00
24 00 0B 10 00 23 00 20 00 6B 41 70 71 22 00 24
00 20 00 0B 05 00 41 80 08 0B

然後直接在控制檯輸入下邊的代碼。

WebAssembly.instantiate(new Uint8Array(`
  00 61 73 6D 01 00 00 00 01 17 05 60 00 01 7F 60
00 00 60 01 7F 00 60 01 7F 01 7F 60 02 7F 7F 01
7F 03 07 06 01 04 00 02 03 00 04 05 01 70 01 02
02 05 06 01 01 80 02 80 02 06 0F 02 7F 01 41 90
88 C0 02 0B 7F 00 41 84 08 0B 07 82 01 09 06 6D
65 6D 6F 72 79 02 00 19 5F 5F 69 6E 64 69 72 65
63 74 5F 66 75 6E 63 74 69 6F 6E 5F 74 61 62 6C
65 01 00 03 61 64 64 00 01 0B 5F 69 6E 69 74 69
61 6C 69 7A 65 00 00 10 5F 5F 65 72 72 6E 6F 5F
6C 6F 63 61 74 69 6F 6E 00 05 09 73 74 61 63 6B
53 61 76 65 00 02 0C 73 74 61 63 6B 52 65 73 74
6F 72 65 00 03 0A 73 74 61 63 6B 41 6C 6C 6F 63
00 04 0A 5F 5F 64 61 74 61 5F 65 6E 64 03 01 09
07 01 00 41 01 0B 01 00 0A 30 06 03 00 01 0B 07
00 20 00 20 01 6A 0B 04 00 23 00 0B 06 00 20 00
24 00 0B 10 00 23 00 20 00 6B 41 70 71 22 00 24
00 20 00 0B 05 00 41 80 08 0B`.trim().split(/[\s\r\n]+/g).map(str => parseInt(str, 16))
)).then(({instance}) ={
  const { add } = instance.exports
  console.log('2 + 4 =', add(2, 4))
})

然後就會看到輸出了 2 + 4 = 6

Web API 嘗試

我們再嘗試一下流式編譯。直接使用之前的斐波納契數字的 fibonacci.wasm 模塊。

首先我們需要提供一個簡單的 HTTP 服務,用來返回 .wasm 文件。

新建一個 node.js 文件。

const http = require('http');
const url = require('url');
const fs = require('fs');
const path =require('path');

const PORT = 8888;  // 服務器監聽的端口號;

const mime = {
  "html""text/html;charset=UTF-8",
  "wasm""application/wasm"  // 當遇到對 ".wasm" 格式文件的請求時,返回特定的 MIME 頭;
};

http.createServer((req, res) ={
  let realPath = path.join(__dirname, `.${url.parse(req.url).pathname}`);
  // 檢查所訪問文件是否存在,且是否可讀;
  fs.access(realPath, fs.constants.R_OK, err ={  
    if (err) {
      res.writeHead(404, { 'Content-Type''text/plain' });
      res.end();
    } else {
      fs.readFile(realPath, "binary"(err, file) ={
        if (err) {
          // 文件讀取失敗時返回 500;          
          res.writeHead(500, { 'Content-Type''text/plain' });
          res.end();
        } else {
          // 根據請求的文件返回相應的文件內容;
          let ext = path.extname(realPath);
          ext = ext ? ext.slice(1) : 'unknown';
          let contentType = mime[ext] || "text/plain";
          res.writeHead(200, { 'Content-Type': contentType });
          res.write(file, "binary");
          res.end();
        }
      });
    }
  });
}).listen(PORT);
console.log("Server is runing at port: " + PORT + ".");

然後來編寫我們的 html 文件,講到斐波那契數字,我們順便做一個性能的測試,來比較一下使用 wasm 的方式和原生 js 的求解速度。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta  />
    <title>斐波納切數字</title>
  </head>
  <script>
    function fibonacciJS(n) {
      if (n < 2) {
        return 1;
      }
      return fibonacciJS(n - 1) + fibonacciJS(n - 2);
    }
    const response = fetch("fibonacci.wasm");
    const num = [5, 15, 25, 35, 45];
    WebAssembly.instantiateStreaming(response).then(
      ({ instance }) ={
        let { fibonacci } = instance.exports;
        for(let n of num) {
          console.log(`斐波納切數字: ${n},運行 10 次`)
          let cTime = 0;
          let jsTime = 0;
          for(let time = 0; time < 10; time++) {
            let start = performance.now();
            fibonacci(n)
            cTime += (performance.now() - start)

            start = performance.now();
            fibonacciJS(n)
            jsTime += (performance.now() - start)
          }
          console.log(`wasm 模塊平均調用時間:${cTime / 10}ms`)
          console.log(`js 模塊平均調用時間:${jsTime / 10}ms`)
        }

      }
    )
  </script>
  <body>
  </body>
</html>

然後執行 node node.js 開啓 http 服務,接着在瀏覽器中打開 http://localhost:8888/index.html,控制檯中輸出如下:

斐波納切數字: 5,運行 10 次
index.html:34 wasm 模塊平均調用時間:0.001499993959441781ms
index.html:35 js 模塊平均調用時間:0.005500001134350896ms
index.html:22 斐波納切數字: 15,運行 10 次
index.html:34 wasm 模塊平均調用時間:0.005999993300065398ms
index.html:35 js 模塊平均調用時間:0.15650001005269587ms
index.html:22 斐波納切數字: 25,運行 10 次
index.html:34 wasm 模塊平均調用時間:0.6239999900572002ms
index.html:35 js 模塊平均調用時間:1.1620000121183693ms
index.html:22 斐波納切數字: 35,運行 10 次
index.html:34 wasm 模塊平均調用時間:70.59700000681914ms
index.html:35 js 模塊平均調用時間:126.21099999523722ms
index.html:22 斐波納切數字: 45,運行 10 次
index.html:34 wasm 模塊平均調用時間:8129.7520000021905ms
index.html:35 js 模塊平均調用時間:16918.658500007587ms

整理成表格看一下:

可以看到 wasm 很明顯的提高了運行速度,運行時間穩定在 js 的一半,當規模達到 45 的時候,wasm 的運行時間比 js 少了整整 8 秒。

這裏也可以看出,如果對於計算密集型的應用,wasm 可以大展身手了。

前端應用

來看一些目前已經成功落地的 WebAssembly 的應用。

  1. eBay 的條形碼掃描

    eBay 在原生應用中有專門的 C++ 庫用於條形碼的掃描,在 H5 中利用開源 JavaScriptBarcodeReader 做了一個帶條形碼掃描功能的 Web 版本。問題是它只有在 20% 的時間表現良好。剩餘的 80% 的時間運行非常緩慢,準確率也不高。

    最終的解決方案是通過 wasm ,將原有的 c++ 庫引入,以及業界十分有名的、基於 C 語言編寫的開源條形碼掃描庫 ZBar 引入,再加上原本的 js 庫,三者協助,最終識別率達到了 100%

    產品上線後的最終效果如下圖所示。

    產品在上線使用了一段時間後,eBay 技術團隊對應用的條形碼掃描情況進行了統計,結果發現有 53% 的成功掃描來自於 ZBar34% 來自於自研的 C++ 庫。剩下的 13% 則來自於第三方的 JavaScript 庫實現。可見,其中通過 Wasm 實現得到的掃描結果佔據了總成功次數的 87%

    更詳細的過程可以參考 WebAssembly 在 eBay 的實踐:速度提升 50 倍。

  2. AutoCAD

    AutoCAD 是一款由將近 40 年曆史的知名桌面端設計軟件,被廣泛地用於土木建築、裝飾裝潢、工業製圖等多個領域中。

    最初基於C++  編譯爲 Java 代碼供 Android 設備使用,最後,在 Google Web Toolkit(一個 Google 開發的可以使用 Java 語言開發 Web 應用的工具集)的幫助下,又將這些 Java 代碼轉譯爲了 Web 平臺可用的 JavaScript 代碼。但最後生成的 Web 應用代碼庫十分龐大,且在瀏覽器中的運行性能並不可觀。這個「粗糙版」的 Web 應用發佈於 2014 年。

    2015 年通過 Asm.js 將原有的 C++ 代碼中的主要功能直接進行編譯移植到到 Web 平臺,性能有了很大的提告。20183 月,基於 Wasm 構建的 AutoCAD Web 也成功誕生,https://web.autocad.com/login。

  3. 谷歌地球

    Google 地球最初使用 C++ 語言在 Windows 平臺上開發。後來移植到了 AndroidiOS 平臺中。2017418 日,經過全新設計的 Google 地球 9.0 發佈。由於採用了 Native Client 技術,剛發佈時僅能在Chrome 中運行。2020227 日,Google 使用 C++ 語言通過 WebAssembly 上重寫了 Google 地球,從此 Google 地球可以在 FirefoxEdge 上運行。

  4. bilibili 上傳視頻的封面

    在 知乎 看到的一個回答。

投稿視頻的時候,當你的視頻還在上傳中,已經可以自由選擇 AI 推薦的封面。這裏採用了 webassembly+AI 的前端整合。

webassembly 負責讀取本地視頻,生成圖片;

tensorflow.js 負責加載 AI 訓練過的 model,讀取圖片並打分。

從完全的服務端架構 => 前端架構 && 服務端兜底。

webassembly 支持解析 99% 以上的視頻編碼格式,速度提升體驗惠及約 50% 的 web 投稿用戶。

作者:Stois Fu
鏈接:https://www.zhihu.com/question/265700379/answer/951118579
來源:知乎
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

由於當前 Wasm 標準下,Wasm 模塊不能直接操縱 dom 元素,所以 WebAssembly  主要應用在了一些計算密集型的場景下,視頻的解碼編碼、圖像處理、涉及到複雜計算的算法、加密算法等等。

不止於 Web

Wasm 除了應用在瀏覽器中,也可以應用到 out-of-web 環境中。通過 WASIWebAssembly System InterfaceWasm 操作系統接口)標準,Wasm 可以直接與操作系統打交道。通過已經在各種環境實現了 WASI 標準的虛擬機,我們就可以將 wasm 用在嵌入式、IOT 物聯網以及甚至雲,AI 和區塊鏈等特殊的領域和場景中。

有了 WASI 標準,文章最開始介紹的當前應用的架構在未來可能會發生質的改變。

上邊架構的最大問題就是各個操作系統不能兼容,同一個app 需要採用不同的語言在不同平臺下各實現一次。

比如一款 A 應用,如果想實現跨平臺的話,我們需要用 java 完成在安卓上的開發,用 Objective-C 實現 iOS 上的開發,用 C# 實現 PC 端的開發… … 也就是下邊的樣子。

但如果有了 wasm ,我們只需要選擇任意一門語言,然後編譯成 wasm,就可以分發到各個平臺上了。

這也是 Wasm 官方宣傳的 Ending 定律,Any application that can be compiled to WebAssembly, will be compiled to WebAssembly eventually.

總結

此時回顧一下,WebAssebmly 的定義,應該會清晰很多了。

WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable compilation target for programming languages, enabling deployment on the web for client and server applications.

它不是一種語言,而是規定了一種虛擬指令集,可以作爲各個語言的編譯目標,然後通過 wasm 的虛擬機運行到瀏覽器還有其他各個平臺中。

對於前端領域,當前 Webassembly 在某些場景下可以有效提高前端項目的性能,並且可以將 C/C++ 領域的一些優秀的庫通過編譯直接運行到瀏覽器中。如果前端遇到了性能的問題,不妨可以考慮下 WebAssmbly 的方案。

參考鏈接

極客時間 WebAssembly 入門課,很系統,強烈推薦

WebAssembly 現狀與實戰

WebAssembly: another JVM?

Why WebAssembly is a Big Deal

Awesome WebAssembly Languages

彙編語言入門教程

WebAssembly 在 eBay 的實踐:速度提升 50 倍

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/rYdcfD3spk4hsBCn2ZDdrg