WasmランタイムをWasm化してWasmランタイムで動かす(Wasm Runtime on Wasm Runtime)

1. はじめに

Wasmは、あらゆる言語で記述されたプログラムをWasmにコンパイルすることで、Wasmランタイムが動作する計算機であればどこでもプログラムが実行できるポータブルな環境である。 JavaのWrite Once, Run Anywhereみたいなことをあらゆる言語で実現できる。 WasmランタイムはWasmtimeやWasmer, WasmEdgeなどクラウドや組み込み、エッジ環境などに合わせた多様な実装がある。

ある日ふと、WasmランタイムをWasmにコンパイルして別のWasmランタイムで実行できるんだろうか?と気になった。ソースコードは改変せず、ビルドスクリプトの変更程度でなんとかなるか試してみた。

2. WasmランタイムをWasmビルドしてみる

2.1 Wasmtime

WasmとWASIの実質的なリファレンス実装。

cargo build --target wasm32-wasi
...
error: failed to run custom build command for `cranelift-codegen v0.109.0 (/home/chikuwait/wasmtime/cranelift/codegen)`

Caused by:
  process didn't exit successfully: `/home/chikuwait/wasmtime/target/debug/build/cranelift-codegen-3ef08dfd75fb413b/build-script-build` (exit status: 101)
  --- stderr
  thread 'main' panicked at cranelift/codegen/build.rs:50:53:
  error when identifying target: "no supported isa found for arch `wasm32`"
  note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Wasmtimeは、Rustで実装されており、WasmモジュールをJITコンパイルして実行する。 JITコンパイルにはCraneliftを使用しており、中間コード(Cranelift IR)からマシンコードを生成するためのcranelift-codegen Crateのビルドに失敗する(WasmバイトコードからWasmバイトコードにコンパイルすることになるので、おかしなことになる)。

参考: cargo wasi run doesn’t work - error with cranelift-codegen v0.96.3 #6540 - bytecodealliance/wasmtime 

2.2 Wasmi

インタプリタ方式を採用したWasmランタイム。軽量で組み込み・IoT環境での使用を意識している。

cargo build --target wasm32-wasi

...
error: failed to run custom build command for `cranelift-codegen v0.105.4`

Caused by:
  process didn't exit successfully: `/home/chikuwait/wasmi/target/debug/build/cranelift-codegen-a4177e815286ec07/build-script-build` (exit status: 101)
  --- stderr
  thread 'main' panicked at /home/chikuwait/.cargo/registry/src/index.crates.io-6f17d22bba15001f/cranelift-codegen-0.105.4/build.rs:48:53:
  error when identifying target: "no supported isa found for arch `wasm32`"
  note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
warning: build failed, waiting for other jobs to finish...

WasmiはWasmtimeのcrateを使用して実装されているため、Wasmtimeと同様にビルドに失敗する。

2.3 Wasmer

ラップトップ(Win/Mac)から、クラウド、エッジなどあらゆる環境でWasmを実行するためのランタイム。WASIを拡張してPOSIXの多様な機能に対応するオリジナルなインターフェースであるWASIXをサポートしている。

cargo build --target wasm32-wasi --manifest-path lib/cli/Cargo.toml
...
error[E0433]: failed to resolve: use of undeclared crate or module `platform`
  --> /home/chikuwait/.cargo/registry/src/index.crates.io-6f17d22bba15001f/rustls-native-certs-0.6.3/src/lib.rs:58:42
   |
58 |     load_certs_from_env().unwrap_or_else(platform::load_native_certs)
   |                                          ^^^^^^^^ use of undeclared crate or module `platform`

Wasmerは、TLSライブラリのRustlsを使用しているが、これは各プラットフォーム(Windows, macOS, Linux)ネイティブの証明書ストアを使用する。 そのため、Wasmはサポートされていない。

参考:use of undeclared crate or module platform #92 - rustls/rustls-native-certs

2.4 WasmEdge

OCI(Open Container Initiative)に対応しているクラウドネイティブなWasmランタイム。 クラウドネイティブ環境(サーバレスやマイクロサービス)やエッジ環境での実行を想定している。

cmake -DCMAKE_C_COMPILER="/home/chikuwait/wasi-sdk/bin/clang" -D CMAKE_CXX_COMPILER="/home/chikuwait/wasi-sdk/bin/clang++" .. -D CMAKE_CXX_COMPILER_TARGET=wasm32-wasi-threads -D CMAKE_C_COMPILER_TARGET=wasm32-wasi-threads

...

CMake Error at /usr/lib/llvm-14/cmake/AddLLVM.cmake:552 (add_library):
  Target "wasmedgeLLVM" links to target "ZLIB::ZLIB" but the target was not
  found.  Perhaps a find_package() call is missing for an IMPORTED target, or
  an ALIAS target is missing?
Call Stack (most recent call first):
  lib/llvm/CMakeLists.txt:50 (llvm_add_library)


CMake Error at cmake/Helper.cmake:184 (add_library):
  Target "wasmedgeVM" links to target "ZLIB::ZLIB" but the target was not
  found.  Perhaps a find_package() call is missing for an IMPORTED target, or
  an ALIAS target is missing?
Call Stack (most recent call first):
  lib/vm/CMakeLists.txt:4 (wasmedge_add_library)


CMake Error at cmake/Helper.cmake:184 (add_library):
  Target "wasmedgeDriver" links to target "ZLIB::ZLIB" but the target was not
  found.  Perhaps a find_package() call is missing for an IMPORTED target, or
  an ALIAS target is missing?
Call Stack (most recent call first):
  lib/driver/CMakeLists.txt:16 (wasmedge_add_library)


CMake Error at cmake/Helper.cmake:184 (add_library):
  Target "wasmedge_shared" links to target "ZLIB::ZLIB" but the target was
  not found.  Perhaps a find_package() call is missing for an IMPORTED
  target, or an ALIAS target is missing?
Call Stack (most recent call first):
  lib/api/CMakeLists.txt:101 (wasmedge_add_library)


CMake Error at cmake/Helper.cmake:184 (add_library):
  Target "wasmedgeCAPI" links to target "ZLIB::ZLIB" but the target was not
  found.  Perhaps a find_package() call is missing for an IMPORTED target, or
  an ALIAS target is missing?
Call Stack (most recent call first):
  lib/api/CMakeLists.txt:75 (wasmedge_add_library)

WasmEdgeはzlibを使用するが、wasi-sdkのsysrootには存在しないのでビルドに失敗する。zlibをWasm向けにビルドしてあげればもしかしたらうまくいくかもしれない。

参考: No zlib in WASI #93819 - python/cpython

2.5 Wasm3

Wasm3は、高速で汎用的なWasmインタプリタ。Arduinoなどの組み込み・IoT環境で実行できることも謳っている。

cmake -DCMAKE_TOOLCHAIN_FILE="/home/chikuwait/wasm3/wasi-sdk-11.0/share/cmake/wasi-sdk.cmake" -DWASI_SDK_PREFIX="/home/chikuwait/wasm3/wasi-sdk-11.0" .. && make

...
[100%] Linking C executable wasm3.wasm
clang version 10.0.0 (https://github.com/llvm/llvm-project d32170dbd5b0d54436537b6b75beaf44324e0c28)
Target: wasm32-unknown-wasi
Thread model: posix
InstalledDir: /home/chikuwait/wasm3/wasi-sdk-11.0/bin
 "/home/chikuwait/wasm3/wasi-sdk-11.0/bin/wasm-ld" -L/home/chikuwait/wasm3/wasi-sdk-11.0/share/wasi-sysroot/lib/wasm32-wasi /home/chikuwait/wasm3/wasi-sdk-11.0/share/wasi-sysroot/lib/wasm32-wasi/crt1.o --no-threads -z stack-size=8388608 CMakeFiles/wasm3.wasm.dir/platforms/app/main.c.obj source/libm3.a -lc /home/chikuwait/wasm3/wasi-sdk-11.0/lib/clang/10.0.0/lib/wasi/libclang_rt.builtins-wasm32.a -o wasm3.wasm
[100%] Built target wasm3.wasm

Wasm3は、Readmeでwasm3 can execute wasm3 (self-hosting)と書かれているように、Wasmにコンパイルできることを公式にアピールしている。 そのため、特にハマるところはなく簡単にwasmにビルドできた。

wasm3 wasm3.wasm hello.wasm
hello, world!

3. Wasm3.wasm on Wasmランタイムのベンチマーク

wasm3がwasmにビルドできたので、wasm3.wasm on Wasmランタイム環境で簡単なベンチマークを実行して性能を計測してみた。

実験環境は、さくらの専用サーバPHY Fujitsu PRIMERGY RX2530 M5を使用した。

  • Intel Silver 4208 2.1GHz (8コア)
  • 32GB RAM
  • Linux 5.15

ベンチマークには、The Computer Language 24.06 Benchmarks GameにあるBinary-treeのRustコードを使用して、計算にかかる時間を測定した。N=7、14、21。

Binary-tree 7 14 21
Wasm3 (Interp.) 0.025s 2.023s 6m10.097s
Wasm3.wasm on Wasm3 0.349s 1m5.761s 200m51.784s
Wasmtime (JIT) 0.450s 0.622s 32.909s
Wasm3.wasm on Wasmtime (JIT) 0.133s 21.677s 67m13.435s
WasmEdge (Interp.) 0.191s 40.264s 125m45.874s
WasmEdge (AOT) 0.043s 0.226s 33.869s
Wasm3.wasm on WasmEdge (Interp.) 4.099s 11m55.544s 2203m10.506s
Wasm3.wasm on WasmEdge (AOT) 0.219s 55.496s 173m24.420s

実験結果は以下のテーブルの通り。 N=21のとき、入れ子しない場合と比べて、Wasm3.wasm on Wasm3は約32倍、Wasm3.wasm on Wasmtime (JIT) は約122倍、 Wasm3.wasm on WasmEdge (Interp.) は、約17倍、Wasm3.wasm on WasmEdge (AOT)は約307倍計算時間がかかった。

想像の通りかなり遅い。 WasmEdgeの結果を見ると、AOTコンパイルでインタプリタ程度には近づけることができるが、とはいえ遅い。 不思議なのは、WasmtimeでN=7の時だけ、Wasm3.wasm on Wasmtimeのほうが速かった。 これはJITコンパイルするホットスポットの違いなのだろうか?ちゃんとプロファイリングを取ってみないと分からない。 JITコンパイルのパフォーマンスは難しい。 ともかく、これだけ差があると、実用的なアプリケーションをランタイムonランタイムするのは中々難しいかもしれない(そんなユースケースはあるか?不明)。




Enjoy Reading This Article?

Here are some more articles you might like to read next:

  • WASI’s Capability-based Security Model
  • Setting Virtualhost Configuration Dynamically Using ngx_mruby
  • How Does Traceroute Get Network Route?