Serverless的未來WASM

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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章