1 什麼是WASM
WASM是WebAssembly的縮寫,WebAssembly是一種用於基於堆棧虛擬機的二進制指令格式。Wasm 被設計爲編程語言的可移植編譯目標,支持在Web上爲客戶端和服務器應用程序服務部署。總結起來WASM是一種可以在現代Web瀏覽器種運行的新型代碼,是一種類似低級彙編的語言,擁有緊湊的二進制格式,能夠以接近本機的性能運行。爲高級語言(C/C++、Go、Rust、Python等)提供編譯目標,通過這種方式Rust編寫的程序就可以在Web中運行。WASM和JS可以一起配合工作。
2 WASM的特點
2.1 高效快速
WASM非常的輕量且加載速度極快。WASM的目標是以原生的速度運行,從而充分利用各種平臺都擁有的通用硬件能力。使用最常見、最普通的硬件提升軟件運行的效率。
2.2 安全
WebAssembly 描述了一個內存安全的沙箱執行環境,甚至可以在現有的 JavaScript 虛擬機中實現。當嵌入到 Web 中時,WebAssembly 將強制執行瀏覽器的同源和權限安全策略。
2.3 開放可調試
WebAssembly 旨在以文本格式漂亮地打印調試信息,用於調試、測試、實驗、優化、學習、教學和手工編寫程序(這個有點酷)。在網絡上查看 Wasm 模塊的源時將使用文本格式(這簡直是調試的魔法糖)。
2.4 開放Web平臺的一部分
WebAssembly 旨在保持 Web 的無版本、功能測試和向後兼容的特性。WebAssembly 模塊能夠在JavaScript上下文內外調用,可以訪問瀏覽器的函數。WebAssembly也支持在非瀏覽器環境運行。
3 WSAI
WASM如此的優秀,偉大的程序員當然不想WASM只能在Web中得到應用,WASM應該在任何系統、設備上得到應用,爲此設計了WASM的模塊化系統接口WASI,通過標準的接口和主機環境進行交互。
4 運行時
WASM的執行依賴運行時環境,目前字節聯盟開發了單機的輕量化運行時wasmtime。wasmtime非常的輕巧,Windows下大小隻有2MB左右。
wasmtime的主要特點包括:
輕量:WASM的單機運行時,可按需擴展。微信芯片和大型服務器都是無縫使用。可內嵌到大多數的應用程序。因爲輕量可以無處不在。
快:建立在優化的 Cranelift 代碼生成器之上,可以在運行時快速生成高質量的機器代碼。
可配置:無論您需要提前預編譯您的 wasm 還是在運行時解釋它,Wasmtime 都能滿足您執行 wasm 的所有需求。
WASI支持:支持豐富的API集合,通過WASI標準和主機環境交互。
標準:Wasmtime 通過了官方的 WebAssembly 測試,實現了 wasm 的官方 C API,也實現了未來對 WebAssembly 的提案。 Wasmtime 開發人員也一直密切參與 WebAssembly 標準流程。
5 快速體驗
5.1 使用Rust編寫WASM
可以使用rust工具將rust源碼編譯爲wasm,所以我們可以用Rust語言編寫程序,然後編譯成wasm格式,然後使用wasmtime運行wasm或者在其他語言比如Go中運行wasm,這聽起來是不是很酷,下面以一個簡單的程序進行說明。
第一步:將WebAssembly設置爲cargo編譯的目標對象
rustup target add wasm32-wasi
第二步:創建一個簡單的Rust項目
cargo new hello-wasm
將main.rs函數修改爲如下:
fn main() {
println!("Hello, WASM")
}
第三步:將Rust代碼編譯成WASM
cargo build --target wasm32-wasi
在hello-wasm/target/wasm32-wasi/debug目錄下可以找到編譯後的文件hello-wasm.wasm
5.2 使用wasmtime運行wasm
在hello-wasm.wasm所在的目錄打開命令行,執行下面的命令運行wasm:
wasmtime hello-wasm.wasm
屏幕上將會正確打印:Hello, WASM
檢查wasmtime是否正確安裝
下面我們按照WASM的規範編寫一個計算最大公約數gcd的函數,然後使用多種熟悉的編程語言去調用gcd函數。
gcd.wat的內容如下:
(module
(func $gcd (param i32 i32) (result i32)
(local i32)
block ;; label = @1
block ;; label = @2
local.get 0
br_if 0 (;@2;)
local.get 1
local.set 2
br 1 (;@1;)
end
loop ;; label = @2
local.get 1
local.get 0
local.tee 2
i32.rem_u
local.set 0
local.get 2
local.set 1
local.get 0
br_if 0 (;@2;)
end
end
local.get 2
)
(export "gcd" (func $gcd))
)
5.3 使用Rust運行gcd函數
第一步:創建gcd項目
cargo new gcd
第二步:添加依賴
[dependencies]
wasmtime = "0.31.0"
anyhow = "1.0.45"
第三步:編寫main.rs代碼運行gcd函數
use anyhow::Result;
use wasmtime::*;
fn main() -> Result<()> {
let mut store = Store::<()>::default();
let module = Module::from_file(store.engine(), "gcd.wat")?;
let instance = Instance::new(&mut store, &module, &[])?;
let gcd = instance.get_typed_func::<(i32, i32), i32, _>(&mut store, "gcd")?;
println!("gcd(6, 27) = {}", gcd.call(&mut store, (6, 27))?);
Ok(())
}
第四步:運行程序查看輸出結果
cargo run
程序將會輸出:
gcd(6, 27) = 3
5.4 使用Bash運行wasm
第一步:編寫gcd.sh
運行gcd函數
#!/bin/bash
function gcd() {
# Cast to number; default = 0
local x=$(($1))
local y=$(($2))
# Invoke GCD from module; suppress stderr
local result=$(wasmtime gcd.wat --invoke gcd $x $y 2>/dev/null)
echo "$result"
}
# main
for num in "27 6" "6 27" "42 12"; do
set -- $num
echo "gcd($1, $2) = $(gcd "$1" "$2")"
done
第二步:運行gcd.sh
sh gcd.sh
程序將會輸出:
gcd(27, 6) = 3
gcd(6, 27) = 3
gcd(42, 12) = 6
5.5 使用Python運行gcd
第一步:安裝wasmtime
pip install wasmtime
第二步:編寫gcd.py
代碼運行gcd函數
from wasmtime import Store, Module, Instance
store = Store()
module = Module.from_file(store.engine, 'gcd.wat')
instance = Instance(store, module, [])
gcd = instance.exports(store)["gcd"]
print("gcd(6, 27) = %d" % gcd(store, 6, 27))
第三步:運行gcd.py
python gcd.py
程序將會輸出:
gcd(6, 27) = 3