Open4

WebAssemblyランタイム比較(Lucet, Wasmer, Wasmtime)

ピン留めされたアイテム

モチベーション

サーバーサイド WebAssembly の文脈で見かけるランタイムとして Lucet, Wasmer, Wasmtime などがあるが、違いがよくわからないので調べたい。

調査すること

以下の Wasm ランタイムを一通りドキュメント読んだりチュートリアルを試す。

参考リンク

全体

Lucet

Wasmer

Wasmtime

Lucet

開発元

Fastly

インストール手順

Announcing Lucet: Fastly’s native WebAssembly compiler and runtime | Fastly
ここに書いてる手順は若干古い?最新のドキュメントは
Overview - Lucet

特徴

セットアップ手順

Lucet で実行可能な Wasm を Rust で書くには

target に wasm32-wasi を指定する。

$ rustup target add wasm32-wasi
$ cargo build --target wasm32-wasi

Rust のファイルはこんな感じ。

#[no_mangle]
pub fn add(a: i32, b: i32) -> i32 {
    println!("{}", a + b);
    return a + b;
}

#[no_mangle]
pub fn hello() {
    println!("hello");
}

fn main() {
    println!("Hello, world from Rust!")
}

lucet-runtime 側は

fn main() {
    // rocket::ignite().mount("/", routes![index, execute, upload]).launch();
    // ensure the WASI symbols are exported from the final executable
    lucet_wasi::export_wasi_funcs();
    // load the compiled Lucet module
    let dl_module = DlModule::load("guest.so").unwrap();
    // create a new memory region with default limits on heap and stack size
    let region = MmapRegion::create(
        1,
        &Limits::default().with_heap_memory_size(10 * 16 * 64 * 1024), // (1)
    )
    .unwrap();
    // instantiate the module in the memory region
    let mut instance = region.new_instance(dl_module).unwrap();
    // prepare the WASI context, inheriting stdio handles from the host executable
    let wasi_ctx = WasiCtxBuilder::new().inherit_stdio().build().unwrap();
    instance.insert_embed_ctx(wasi_ctx);
    // run the WASI main function
    // instance.run("main", &[]).unwrap();
    let retval = instance
        .run("add", &[5i32.into(), 3i32.into()]) // (2)
        .unwrap()
        .unwrap_returned();
    println!("{}", i32::from(retval)); // (3)
}

(1) with_heap_memory_size() を指定しないと以下のエラーが出たので指定している

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: LimitsExceeded("heap spec initial size: HeapSpec { reserved_size: 4194304, guard_size: 4194304, initial_size: 1114112, max_size: None }")'

(2), (3) 引数および戻り値の型は
lucet_runtime - Rust
を読む限り明示的に指定する必要がありそう。

未解決

  • Lucet で実行可能な Wasm を Rust で書くには...

🚨 lucet-runtime が Lucet リポジトリ内じゃないと動かない

やりたいこと

Using the Lucet runtime API from Rust - Lucet

  • これのとおりに lucet-runtime を動かしたい
  • そのために cargo new で独立したプロジェクトを作る
  • サンプルは相対パス指定になっているのでバージョンを指定する

試したこと

1)2021-04-26 時点で lucet-runtime および lucet-wasi の最新バージョンは 0.6.1

https://docs.rs/lucet-runtime/0.6.1/lucet_runtime/
https://docs.rs/lucet-wasi/0.6.1/lucet_wasi/

Cargo.toml でこのバージョンを指定すると以下のエラー

[dependencies]
lucet-runtime = "0.6.1"
lucet-wasi = "0.6.1"
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: DlError(Custom { kind: Other, error: "dlopen(/Users/yamazaki/repos/github.com/zaki-yama-labs/lucet-playground/lucet-runtime-example/example.so, 2): Symbol not found: _hostcall_wasi_snapshot_preview1_fd_close\n  Referenced from: /Users/yamazaki/repos/github.com/zaki-yama-labs/lucet-playground/lucet-runtime-example/example.so\n  Expected in: flat namespace\n in /Users/yamazaki/repos/github.com/zaki-yama-labs/lucet-playground/lucet-runtime-example/example.so" })', src/main.rs:8:50
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

2)リポジトリの最新のコミットで試す

[dependencies]
lucet-runtime = { git = "https://github.com/bytecodealliance/lucet", branch = "main" }
lucet-wasi = { git = "https://github.com/bytecodealliance/lucet", branch = "main" }

結果は別のエラー

error[E0599]: the method `set_times` exists for struct `cap_std::fs::File`, but its trait bounds were not satisfied
  --> /Users/yamazaki/.cargo/git/checkouts/lucet-d29c7ec0f6d2e0d4/51fb1ed/wasmtime/crates/wasi-common/cap-std-sync/src/file.rs:84:14
   |
84 |             .set_times(convert_systimespec(atime), convert_systimespec(mtime))?;
   |              ^^^^^^^^^ method cannot be called on `cap_std::fs::File` due to unsatisfied trait bounds
   |
  ::: /Users/yamazaki/.cargo/registry/src/github.com-1ecc6299db9ec823/cap-std-0.13.9/src/fs/file.rs:32:1
   |
32 | pub struct File {
   | ---------------
   | |
   | doesn't satisfy `_: unsafe_io::unsafe_handle::AsUnsafeFile`
   | doesn't satisfy `cap_std::fs::File: SetTimes`
   |
   = note: the following trait bounds were not satisfied:
           `cap_std::fs::File: unsafe_io::unsafe_handle::AsUnsafeFile`
           which is required by `cap_std::fs::File: SetTimes`
   = help: items from traits can only be used if the trait is in scope
   = note: the following trait is implemented but not in scope; perhaps add a `use` for it:
           `use fs_set_times::set_times::SetTimes;`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0599`.
error: could not compile `wasi-cap-std-sync`

To learn more, run the command again with --verbose.
warning: build failed, waiting for other jobs to finish...
error: build failed

Wasmer

開発元

Wasmer社 https://wasmer.io/

インストール

Getting Started - Wasmer Docs

$ curl https://get.wasmer.io -sSfL | sh

CLI

CLI Usage - Wasmer Docs

fn main() {
    println!("Hello, world!");
}
$ cargo build --target wasm32-wasi
$ wasmer target/wasm32-wasi/debug/wasmer-playground.wasm
Hello, world!

単純にwasmファイルを実行する以外にもさまざまなサブコマンドがありそう。

To do this, you want to find a Wasm Module compiled down to an ABI that the Wasmer runtime supports, such as WASI or Emscripten. For instance, we can search for a module on WAPM, and go to the module page, and then click on the "Browse modules" tab.

ABIとはApplication Binary Interfaceの略(のはず)。

Features

Wasmer Features - Wasmer Docs

  • Compilers: 複数のコンパイラをサポート
  • Caching
  • Metering 計算時間やその他の資源はモニタリングでき、コントロールできる
  • WebAssembly Features:
    • Bulk-memory operations
    • Multi-value return
    • ...
  • ABIs
    • Emscripten
    • WASI

Language Integrations

Rust - Wasmer Docs

Lucet と同様、Rust プログラムから使うためのライブラリも提供されてそう。

その他

Examples
が充実してたり、Lucet に比べドキュメントはしっかりしてる印象。

TODO

ログインするとコメントできます