WebAssembly 快速指南



WebAssembly - 概述

WebAssembly 是一種用於 Web 的新型計算機程式語言。WebAssembly 程式碼是一種低階二進位制格式,與 Web 相容,並且可以輕鬆地在現代 Web 瀏覽器中執行。生成的​​檔案大小很小,並且載入和執行速度更快。您現在可以將 C、C++、Rust 等語言編譯為二進位制格式,並且它可以在 Web 上像 javascript 一樣執行。

WebAssembly 的定義

根據 WebAssembly 的官方網站(可在 https://webassembly.org/ 獲取),其定義為 WebAssembly(縮寫為 Wasm)是一種基於棧的虛擬機器的二進位制指令格式。Wasm 被設計為 C/C++/Rust 等高階語言的行動式編譯目標,使客戶端和伺服器應用程式能夠在 Web 上部署。

Web Assembly 不是開發人員必須編寫的語言,而是用 C、C++、Rust 等語言編寫的程式碼,可以編譯成 WebAssembly (wasm)。相同的程式碼可以在 Web 瀏覽器內部執行。

Web Assembly 是一種新的語言,程式碼是低階組合語言,但憑藉其文字格式功能,程式碼具有可讀性,並且如果需要,可以進行除錯。

WebAssembly 的目標

WebAssembly 的開放標準是在一個 W3C 社群小組中開發的,該小組包括來自所有主要瀏覽器的代表以及一個 W3C 工作組。

WebAssembly 的主要目標如下所示 -

  • 更快高效且可移植 - WebAssembly 程式碼旨在利用可用的硬體在不同的平臺上更快地執行。

  • 易於閱讀和除錯 - WebAssembly 作為一種低階組合語言,支援文字格式,允許您除錯程式碼中的任何問題,並在必要時重寫程式碼。

  • 安全性 - WebAssembly 在 Web 瀏覽器上執行是安全的,因為它會處理許可權和同源策略。

WebAssembly 的優勢

以下是 WebAssembly 的優勢 -

  • 在現代瀏覽器上執行 - WebAssembly 能夠在可用的現代 Web 瀏覽器上毫無問題地執行。

  • 支援多種語言 - C、C++、Rust、Go 等語言現在可以將程式碼編譯為 WebAssembly,並在 Web 瀏覽器中執行。因此,以前無法在瀏覽器中執行的語言現在可以實現了。

  • 更快、高效且可移植 - 由於程式碼體積小,因此載入和執行速度更快。

  • 易於理解 - 開發人員不必花費太多精力來理解 WebAssembly 編碼,因為他們不必用 WebAssembly 編寫程式碼。而是將程式碼編譯為 WebAssembly,並在 Web 上執行。

  • 易於除錯 - 儘管最終程式碼是低階組合語言,但您也可以將其獲取為易於閱讀和除錯的文字格式。

WebAssembly 的缺點

以下是 WebAssembly 的缺點 -

  • WebAssembly 仍在開發中,現在就斷言其未來還為時過早。

  • WebAssembly 依賴於 javascript 與文件物件模型 (DOM) 進行互動。

WebAssembly - 簡介

WebAssembly 也稱為 WASM,於 2017 年首次推出。WebAssembly 起源背後的主要科技公司包括 Google、Apple、Microsoft、Mozilla 和 W3C。

坊間傳聞稱 WebAssembly 由於其更快的執行速度將取代 Javascript,但事實並非如此。WebAssembly 和 Javascript 旨在協同工作以解決複雜問題。

WebAssembly 的需求

到目前為止,只有 Javascript 可以在瀏覽器內部成功工作。瀏覽器中有一些非常繁重的任務難以使用 javascript 執行。

舉幾個例子,例如影像識別、計算機輔助設計 (CAD) 應用程式、即時影片增強、VR 和增強現實、音樂應用程式、科學視覺化和模擬、遊戲、影像/影片編輯等。

WebAssembly 是一種具有二進位制指令的新語言,可以更快地載入和執行。上述任務可以使用 C、C++、Rust 等高階語言輕鬆完成。我們需要一種方法,將我們用 C、C++、Rust 編寫的程式碼編譯並用於 Web 瀏覽器。使用 WebAssembly 可以實現這一點。

當 WebAssembly 程式碼載入到瀏覽器內部時。然後,瀏覽器負責將其轉換為處理器可以理解的機器格式。

對於 javascript,程式碼必須下載、解析並轉換為機器格式。這需要花費大量時間,對於我們之前提到的繁重任務來說,速度可能會非常慢。

WebAssembly 的工作原理

像 C、C++ 和 Rust 這樣的高階語言被編譯成二進位制格式,即 .wasm 和文字格式 .wat

Working of WebAssembly

用 C、C++ 和 Rust 編寫的原始碼使用編譯器編譯為 .wasm。您可以使用 Emscripten SDK 將 C/C++ 編譯為 .wasm

流程如下 -

Wasm

可以使用 Emscripten SDK 將 C/C++ 程式碼編譯為 .wasm。隨後,可以在 html 檔案中藉助 javascript 使用 .wasm 程式碼來顯示輸出。

WebAssembly 的關鍵概念

關鍵概念解釋如下 -

模組

模組是由瀏覽器編譯為可執行機器程式碼的物件。模組被認為是無狀態的,可以在視窗和 Web 工作執行緒之間共享。

記憶體

WebAssembly 中的記憶體是一個arraybuffer,用於儲存資料。您可以使用 Javascript api WebAssembly.memory() 分配記憶體。

WebAssembly 中的表是一個型別化陣列,即 WebAssembly 記憶體外部,並且主要包含對函式的引用。它儲存函式的記憶體地址。

例項

例項是一個物件,它將包含所有可以從 javascript 呼叫的匯出函式,以便在瀏覽器中執行。

WebAssembly - WASM

WebAssembly 也稱為 wasm,是對 Javascript 的改進。它被設計為像 javascript 一樣在瀏覽器中執行,也可以在 nodejs 中執行。當任何高階語言(如 C、C++、Rust)編譯時,您會獲得 wasm 輸出。

考慮以下 C 程式 -

int factorial(int n) {
   if (n == 0) 
      return 1; 
   else 
      return n * factorial(n-1); 
}

使用 WasmExplorer(可在 https://mbebenita.github.io/WasmExplorer/ 獲取)獲取如下所示的編譯程式碼 -

Wasm Explorer

階乘程式的 WebAssembly 文字格式如下所示 -

(module 
   (table 0 anyfunc) 
   (memory $0 1) 
   (export "memory" (memory $0)) (export "factorial" (func $factorial)) 
   (func $factorial (; 0 ;) (param $0 i32) (result i32)
      (local $1 i32) 
      (local $2 i32) 
      (block $label$0 
         (br_if $label$0 
            (i32.eqz 
               (get_local $0) 
            )
         )
         (set_local $2 
            (i32.const 1) 
         ) 
         (loop $label$1 
            (set_local $2 
               (i32.mul 
                  (get_local $0) (get_local $2) 
               ) 
            ) 
            (set_local $0 
               (tee_local $1        (i32.add 
                  (get_local $0) (i32.const -1) 
               ) 
               ) 
            ) 
            (br_if $label$1      (get_local $1) 
            ) 
         ) 
         (return 
            (get_local $2)
         ) 
      ) 
      (i32.const 1) 
   )
)

使用 Wat2Wasm 工具,您可以檢視 WASM 程式碼,就像下面提到的那樣 -

Wat2Wasm Tool

開發人員不應該用 wasm 編寫程式碼或學習用它編寫程式碼,因為它主要是在編譯高階語言時生成的。

棧式機器模型

在 WASM 中,所有指令都推送到棧上。引數會被彈出,結果會被推回棧上。

考慮以下 WebAssembly 文字格式,它將兩個數字相加 -

(module
   (func $add (param $a i32) (param $b i32) (result i32) 
      get_local $a 
      get_local $b 
      i32.add
   )
   (export "add" (func $add))
)

函式的名稱為 $add,它接收 2 個引數 $a 和 $b。結果為 32 位整數型別。區域性變數使用 get_local 訪問,加法運算使用 i32.add 執行。

執行期間將兩個數字相加的棧表示如下 -

Stack

步驟 1中 - 執行 get_local $a 指令,第一個引數即 $a 被推送到棧上。

步驟 2中 - 在執行 get_local $b 指令期間,第二個引數即 $b 被推送到棧上。

步驟 3中 - i32.add 的執行將從棧中彈出元素,並將結果推回棧上。最後棧中保留的值是函式 $add 的結果。

WebAssembly - 安裝

在本節中,我們將學習如何安裝 Emscripten SDK 來編譯 C/C++。Emscripten 是一種低階虛擬機器 (LLVM),它接收從 C/C++ 生成的位元組碼並將其編譯成可以輕鬆地在瀏覽器中執行的 JavaScript。

要將 C/C++ 編譯為 WebAssembly,我們首先需要安裝 Emscripten sdk。

安裝 Emscripten sdk

安裝 Emscripten sdk 的步驟如下 -

步驟 1 - 克隆 emsdk 倉庫:git clone https://github.com/emscripten-core/emsdk.git

E:\wa>git clone https://github.com/emscripten-core/emsdk.git 
Cloning into 'emsdk'... 
remote: Enumerating objects: 14, done. 
remote: Counting objects: 100% (14/14), done. 
remote: Compressing objects: 100% (12/12), done. 
remote: Total 1823 (delta 4), reused 4 (delta 2), pack-reused 1809 receiving obje 
cts: 99% (1819/1823), 924.01 KiB | 257.00 KiB/s 
Receiving objects: 100% (1823/1823), 1.01 MiB | 257.00 KiB/s, done. 
Resolving deltas: 100% (1152/1152), done.

步驟 2 - 進入 emsdk 目錄。

cd emsdk

步驟 3 - 對於 Windows:執行以下命令。

emsdk install latest

對於 Linux,此命令將花費一些時間來安裝必要的工具,例如 java、python 等。請遵循以下程式碼 -

./emsdk install latest

步驟 4 - 要啟用最新的 SDK,請在終端中執行以下命令。

對於 Windows,執行以下命令 -

emsdk activate latest

對於 Linux,執行以下命令 -

./emsdk activate latest

步驟 5 - 要啟用 PATH 和其他環境變數,請在終端中執行以下命令。

對於 Windows,執行命令 -

emsdk_env.bat

對於 Linux,執行以下命令 -

source ./emsdk_env.sh

我們已經完成了 emsdk 的安裝,現在可以編譯 C 或 C++ 程式碼了。C/C++ 的編譯將在後面的章節中進行。

要編譯任何 C 或 C++ 程式碼,以下為命令 -

emcc source.c or source.cpp -s WASM=1 -o source.html

輸出將為您提供 source.html 檔案、source.js 和 source.wasm 檔案。js 將包含用於獲取 source.wasm 的 api,當您在瀏覽器中點選 source.html 時,您可以看到輸出。

要僅獲取 wasm 檔案,可以使用以下命令。此命令將僅為您提供 source.wasm 檔案。

emcc source.c or source.cpp -s STANDALONE_WASM

WebAssembly - 編譯為 WASM 的工具

本章將討論一些易於使用的工具,這些工具在使用 WebAssembly 時非常有用。讓我們從學習 WebAssembly.studio 工具開始。

WebAssembly.studio

此工具允許您將 C、Rust、Wat 編譯為 Wasm 等。

WebAssembly Studio

首先,您可以點選 Empty C Project、Empty Rust Project、Empty Wat Project 將 C 和 Rust 編譯為 WASM。5。

Empty C Project

它具有 Build、Run 功能,用於構建程式碼並檢查輸出。下載按鈕允許您下載.wasm檔案,該檔案可用於在瀏覽器中進行測試。此工具非常有助於編譯 C 和 Rust 程式碼並檢查輸出。

WebAssembly Explorer

WebAssembly Explorer 允許您編譯 C 和 C++ 程式碼。有關更多詳細資訊,請參閱連結 https://mbebenita.github.io/WasmExplorer/。點選連結後將顯示如下螢幕 -

WebAssembly Explorer

您可以選擇 C 和 C++ 版本。C 或 C++ 的原始碼在此處編寫 -

Compile Button

點選 Compile 按鈕後,它會在下面的塊中給出 WebAssembly 文字格式 (WAT) 和 Firefox x86 彙編程式碼 -

WAT

您可以下載.wasm程式碼並在瀏覽器中進行測試。

WASMFiddle

Wasmfiddle 幫助你將 C 程式碼編譯成 WebAssembly,並測試輸出結果。點選連結 https://wasmfiddle.com/ 後,你將看到以下頁面:

WASM Fiddle

點選“構建”來編譯程式碼。你可以點選“Wat”和“Wasm”來下載 Wat 和 Wasm 程式碼。要測試輸出結果,請點選“執行”按鈕。

WASM 到 WAT

工具 **wat2wasm** 會在你輸入 WebAssembly 文字格式時生成 wasm 程式碼。你可以點選連結 https://webassembly.github.io/wabt/demo/wat2wasm/ 檢視演示,出現的螢幕如下所示:

WASM to WAT

你可以使用上傳按鈕上傳 .wasm 檔案,文字區域將顯示文字格式。

WAT 到 WASM

工具 wat2wasm 會在你輸入 WebAssembly 文字格式時生成 wasm 程式碼。你可以點選連結 https://webassembly.github.io/wabt/demo/wat2wasm/ 檢視演示,出現的螢幕如下所示:

WAT to WASM

這個工具非常有用,因為它可以幫助你獲得並測試輸出結果。你可以輸入 WAT 程式碼,檢視 .wasm 程式碼,並執行程式碼以檢視輸出。

WebAssembly - 程式結構

WebAssembly,也稱為 WASM,是一種二進位制格式的低階程式碼,旨在以最有效的方式在瀏覽器中執行。WebAssembly 程式碼由以下概念構成:

  • 型別
  • 指令

讓我們詳細瞭解一下。

WebAssembly 中的值用於儲存複雜資料,例如文字、字串和向量。WebAssembly 支援以下型別:

  • 位元組
  • 整數
  • 浮點數
  • 名稱

位元組

位元組是 WebAssembly 支援的最簡單形式的值。值採用十六進位制格式。

例如

表示為 *b* 的位元組,也可以取自然數 n,其中 n < 256。

byte ::= 0x00| .... |0xFF

整數

在 WebAssembly 中,支援的整數如下所示:

  • i32:32 位整數
  • i64:64 位整數

浮點數

在 WebAssembly 中,支援的浮點數如下所示:

  • f32:32 位浮點數
  • f64:64 位浮點數

名稱

名稱是字元序列,其標量值由 Unicode 定義,可以在此處提供的連結 http://www.unicode.org/versions/Unicode12.1.0/ 中找到。

型別

WebAssembly 中的實體被分類為型別。支援的型別如下所示:

  • 值型別
  • 結果型別
  • 函式型別
  • 限制
  • 記憶體型別
  • 表型別
  • 全域性型別
  • 外部型別

讓我們逐一學習它們。

值型別

WebAssembly 支援的值型別如下所示:

  • i32:32 位整數
  • i64:64 位整數
  • f32:32 位浮點數
  • f64:64 位浮點數
valtype ::= i32|i64|f32|f64

結果型別

括號內編寫的值會被執行並存儲在結果型別中。結果型別是程式碼塊(由值組成)執行的輸出。

resulttype::=[valtype?]

函式型別

函式型別將接收引數向量並返回結果向量。

functype::=[vec(valtype)]--> [vec(valtype)]

限制

限制是與記憶體和表型別關聯的儲存範圍。

limits ::= {min u32, max u32}

記憶體型別

記憶體型別處理線性記憶體和大小範圍。

memtype ::= limits

表型別

表型別根據分配給它的元素型別進行分類。

tabletype ::= limits elemtype
elemtype ::= funcref

表型別取決於分配給它的最小和最大大小的限制。

全域性型別

全域性型別儲存具有可以更改或保持不變的值的全域性變數。

globaltype ::= mut valtype
mut ::= const|var

外部型別

外部型別處理匯入和外部值。

externtype ::= func functype | table tabletype | mem memtype | global globaltype

指令

WebAssembly 程式碼是一系列遵循堆疊機器模型的指令。由於 WebAssembly 遵循堆疊機器模型,因此指令會被壓入堆疊。

例如,函式的引數值會從堆疊中彈出,結果會被壓回堆疊。最終,堆疊中只有一個值,即結果。

一些常用的指令如下所示:

  • 數值指令
  • 變數指令

數值指令

數值指令是對數值執行的操作。

例如
nn, mm ::= 32|64
ibinop ::= add|sub|mul|div_sx|rem_sx|and|or|xor
irelop ::= eq | ne | lt_sx | gt_sx | le_sx | ge_sx
frelop ::= eq | ne | lt | gt | le | ge

變數指令

變數指令與訪問區域性變數和全域性變數有關。

例如

訪問區域性變數:

get_local $a
get_local $b

設定區域性變數:

set_local $a
set_local $b

訪問全域性變數:

get_global $a
get_global $b

設定全域性變數:

set_global $a
set_global $b

WebAssembly - JavaScript

本章將列出 WebAssembly 和 Javascript 之間的比較。

Javascript 是一種我們在瀏覽器中大量使用的語言。現在,隨著 WebAssembly 的釋出,我們也可以在瀏覽器中使用 WebAssembly。

WebAssembly 的出現並非為了取代 javascript,而是為了處理 javascript 難以處理的某些事情。

例如

使用 javascript 難以完成影像識別、CAD 應用、即時影片增強、VR 和增強現實、音樂應用、科學視覺化和模擬、遊戲、影像/影片編輯等任務。

使用現在可以編譯成 WebAssembly 的高階語言(如 C/C++、Rust),可以輕鬆完成上述任務。WebAssembly 生成易於在瀏覽器中執行的二進位制程式碼。

因此,以下是 Javascript 和 WebAssembly 之間的比較列表。

引數 Javascript WebAssembly

編碼

你可以輕鬆地用 Javascript 編寫程式碼。編寫的程式碼是人類可讀的,並儲存為 .js 檔案。在瀏覽器中使用時,你需要使用 `