The Issue of the Day Before

不在瀏覽器上執行 wasm

wasm -

nodejs 不在瀏覽器上的 javascript 執行環境。 那想不在瀏覽器上的執行 WebAssembly 要怎麼辦?

Why

What

Wasmtime

Wasmtime 一個 Bytecode Alliance 項目,用 Rust 寫的 wasm runtime。

WAMR

WebAssembly Micro Runtime 另一套 Bytecode Alliance 項目,但用 C 寫的 wasm runtime。

Wasm3

Wasm3C 寫的 wasm runtime。

WasmEdge

WasmEdgeC++ 寫的 wasm runtime。

Wasmer

WasmerRust 寫的 wasm runtime。

project

Wasmtime

WAMR

Wasm3

WasmEdge

Wasmer

lang

Rust

C

C

C++

Rust

org

BA

BA

CNCF

JIT

V

V

V

AOT

V

V

V

WASI

V

V

V

V

V

size

~19M

~300K

~180K

~1.4M

~58M

version

v0.38.1

fast-jit-06-29-2022

v0.5.0

v0.10.0

v2.3.0

C 寫的就是小,Rust 就是大。 很計較空間的地方就建議用 Wasm3,夠小夠大,提供的 APE 格式可以跨多種平台執行而不需要重新編譯。

How

wasm 的本體是二進位的。想看得懂,要將他轉成文字格式的 wat

而一個計算乘階的函式,大概是長下面這樣子的。

factorial.wat
(func (param i64) (result i64)
  local.get 0
  i64.eqz
  if (result i64)
      i64.const 1
  else
      local.get 0
      local.get 0
      i64.const 1
      i64.sub
      call 0
      i64.mul
  end)

下面是等效的 C 程式碼。

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

這是像S表達式的低階程式語言。用它來寫程式當然不太現實。

用甚麼語言來寫?

因為某種不可告人的原因, rustwasm 目前是好姊妹。

所以先裝 link:/install-rust/ [rust]。

rust 的編譯工具 rustc 不只能將 rust 程式碼編譯為執行檔,也能編成 wasm

檢查一下目前可以使用編譯目標。

> rustup target list | grep wasm
// wasm32-unknown-emscripten
// wasm32-unknown-unknown
// wasm32-wasi

要編成可獨立運作的故選擇將 wasm32-wasi 加入編譯清單。

> rustup target add wasm32-wasi

先用產生器產生一個 hello world 程式。

> cargo init hello
> cd hello
> cargo run
// Hello, world!

現在我們有一個用 rust 寫的簡單 hello world 程式。

使用 rust 交叉編譯的功能,將他編譯為 wasm

> cargo build --target wasm32-wasi

在路徑 target/wasm32-wasi/debug/ 中應該能找到 hello.wasm

最後,可以用上述的各種 wasm runtime 來執行。

Wasmtime

Wasmtime
// install
> curl https://wasmtime.dev/install.sh -sSf | bash
// load env
> . ~/.bashrc
// run
> wasmtime target/wasm32-wasi/debug/hello.wasm

WAMR

WAMR
> git clone  https://github.com/bytecodealliance/wasm-micro-runtime.git
> cd wasm-micro-runtime/product-mini/platforms/linux // (1)
> mkdir build
> sudo apt install -y build-essential cmake g++-multilib libgcc-8-dev lib32gcc-8-dev
> cd build
> cmake ..
> make
// copy iwasm to PATH
> iwasm target/wasm32-wasi/debug/hello.wasm
  1. 切換到符合你 OS 的目錄

Wasm3

Wasm3
> wget https://github.com/wasm3/wasm3/releases/download/v0.5.0/wasm3-cosmopolitan.com
> mv wasm3-cosmopolitan.com wasm3
> sudo chmod 755 wasm3
> wasm3 target/wasm32-wasi/debug/hello.wasm

WasmEdge

WasmEdge
> curl -sSf https://raw.githubusercontent.com/WasmEdge/WasmEdge/master/utils/install.sh | bash
> . ~/.wasmedge/env
> wasmedge target/wasm32-wasi/debug/hello.wasm

Wasmer

Wasmer
> curl https://get.wasmer.io -sSfL | sh
// or
> cargo install wasmer-cli
> . ~/.wasmer/wasmer.sh
>  wasmer target/wasm32-wasi/debug/hello.wasm

Shrink Size

rustc 產生的 wasm 有些太大了。可以在正式編譯時加入下面的設定,可以大幅減小最後釋出版本的大小。

利用編譯指示

Cargo.toml
[profile.release]
lto = true  // (1)
opt-level = 's' // (2)
  1. 指示 LLVM 啟用 link-time-optimizations (連結時間優化)

  2. 以 (s)ize 作為優化標準,也可以用 z ,但兩者的結果不一定誰比較小,要試試看。

利用 wasm-opt 工具也能再進一步降低大小

下面指令,主要作用是刪掉 wsam 中的 debuginfonames 區段。

> wasm-opt -Os -o output.wasm input.wasm

覺得手動下指令麻煩,可以安裝 cargo-wasi 來幫忙。

Cargo.toml
[package.metadata]
wasm-opt = true
wasm-name-section = false
wasm-producers-section = false
> cargo install cargo-wasi
> cargo wasi build
閱讀在雲端