swiftwasmで作ったwasmをwasmerで動かす
先月1.0がリリースされたwasmerを使って,swiftwasmで作ったwasmを実行してみました.
swiftで作ったexecutableとの実行速度比較やwasmをpythonに組み込むことも試してみましたが,これらはちょっとうまくいきませんでした.
環境
- macOS: 10.15 (Catalina)
- swiftwasm: 5.3
- wasmer: 1.0.1
swiftwasmのインストール
こちらに従ってインストールします.
swiftwasmのツールチェインがインストールされるので,そのツールチェインを指定してswiftcできるようになります.
$ xcrun --toolchain swiftwasm swiftc --version
SwiftWasm Swift version 5.3 (swiftlang-5.3.1)
Target: x86_64-apple-darwin19.6.0
ただ,私の環境ではxcrunを使うとswiftc実行事にエラーが発生したので,PATHを通して実行するようにしました.
$ xcrun --toolchain swiftwasm swiftc -target wasm32-unknown-wasi -o main.wasm Main.swift
wasm-ld: error: cannot open crt1.o: No such file or directory
wasm-ld: error: unable to find library -ldl
wasm-ld: error: unable to find library -lc++
wasm-ld: error: unable to find library -lc++abi
wasm-ld: error: unable to find library -lm
wasm-ld: error: unable to find library -lwasi-emulated-mman
wasm-ld: error: unable to find library -lc
clang-10: error: linker command failed with exit code 1 (use -v to see invocation)
<unknown>:0: error: link command failed with exit code 1 (use -v to see invocation)
$ export PATH=/Library/Developer/Toolchains/swift-wasm-5.3.1-RELEASE.xctoolchain/usr/bin/:$PATH
$ swiftc -target wasm32-unknown-wasi -o main.wasm Main.swift
wasmerのインストール
こちらに従ってインストールします.
$ curl https://get.wasmer.io -sSfL | sh
Welcome to the Wasmer bash installer!
ww
wwwww
ww wwwwww w
wwwww wwwwwwwww
ww wwwwww w wwwwwww
wwwww wwwwwwwwww wwwww
wwwwww w wwwwwww wwwww
wwwwwwwwwwwwww wwwww wwwww
wwwwwwwwwwwwwww wwwww wwwww
wwwwwwwwwwwwwww wwwww wwwww
wwwwwwwwwwwwwww wwwww wwwww
wwwwwwwwwwwwwww wwwww wwww
wwwwwwwwwwwwwww wwwww
wwwwwwwwwwww wwww
wwwwwwww
wwww
downloading: wasmer-darwin-amd64
Latest release: 1.0.1
Downloading archive from https://github.com/wasmerio/wasmer/releases/download/1.0.1/wasmer-darwin-amd64.tar.gz
######################################################################## 100.0%##O#- #
installing: /Users/otti/.wasmer
Updating bash profile /Users/otti/.zshrc
we've added the following to your /Users/otti/.zshrc
If you have a different profile please add the following:
# Wasmer
export WASMER_DIR="/Users/otti/.wasmer"
[ -s "$WASMER_DIR/wasmer.sh" ] && source "$WASMER_DIR/wasmer.sh"
check: wasmer 1.0.1 installed successfully ✓
wasmer & wapm will be available the next time you open the terminal.
If you want to have the commands available now please execute:
source /Users/otti/.wasmer/wasmer.sh
$ wasmer --version
wasmer 1.0.1
wasm化するプログラム
フィボナッチ数を計算するサンプルをよく見かけたのですが,同じだとつまらないので,前4項の和で決定されるテトラナッチ数を計算するプログラムを書きました.
@_cdecl("tetranacci")
func tetranacci(_ n: Int) -> Int {
switch n {
case 1, 2, 3: return 0
case 4: return 1
default: return [1, 2, 3, 4].reduce(0) {$0 + tetranacci(n - $1)}
}
}
print(tetranacci(Int(CommandLine.arguments[1])!))
他のプログラミング言語に組み込むために1行目でtetranacci
関数をexportしてますが,後述するようにうまくimportはできませんでした・・・.
wasm化
$ export PATH=/Library/Developer/Toolchains/swift-wasm-5.3.1-RELEASE.xctoolchain/usr/bin/:$PATH
$ swiftc -target wasm32-unknown-wasi -o main.wasm Main.swift
$ ls -lh main.wasm
-rwxr-xr-x@ 1 otti staff 9.8M Feb 6 22:24 main.wasm*
$ file main.wasm
main.wasm: WebAssembly (wasm) binary module version 0x1 (MVP)
wasmerでの実行
wasmerで実行するには,できたwasmを渡してあげるだけです.(10番目のテトラナッチ数を計算しました.)
$ wasmer main.wasm 10
29
速度比較
wasmはネイティブ並みに早いということだったので,28番目のテトラナッチ数列を計算する実行で速度比較してみました. 28番目にしたのは,それくらいがちょうどよい実行時間だったからです.
対象 | 実行速度(sec) |
---|---|
swift executable(最適化なし) | 6.9 |
swift executable(最適化有り) | 0.4 |
wasmer JIT (LLVM) | 35.0 |
wasmer JIT (cranelift) | 7.5 |
wasmer JIT (singlepass) | 8.8 |
wasmer 事前コンパイル (LLVM) | 3.7 |
wasmer 事前コンパイル (cranelift) | 7.0 |
wasmer 事前コンパイル (singlepass) | 8.9 |
wasmerを使った場合,LLVMの事前コンパイルが一番速かったです. ただし,事前コンパイルで確かに速くなるけど,最適化オプション付きのexecutableには全く歯が立たない,という感じでした. (もしかすると,なにかやり方が間違ってるのかもしれません.)
また,事前コンパイルはJITエンジンを使った場合はうまくいくのですが,nativeエンジンを使った場合はcraneliftでしかうまくいきませんでした. (上記表はJITエンジンでの事前コンパイル.) コンパイラそれぞれで,以下のような状況でした.
- LLVMはコンパイルコマンドが終わらない
- craneliftは同じくらいの実行速度
- singlepassはコンパイルできるが,実行時エラー
create-exeでexecutableを作ることも試してみましたが,これもnativeエンジンを使った事前コンパイルと同様でした.
それぞれ次のようなコマンドを実行しました.
# swift executable(最適化なし)
$ xcrun --toolchain swift swiftc Main.swift
$ time ./Main 28
# swift executable(最適化有り)
$ xcrun --toolchain swift swiftc -O Main.swift
$ time ./Main 28
# wasmer JIT (LLVM)
$ time wasmer run --llvm main.wasm 28
# wasmer JIT (cranelift)
$ time wasmer run --cranelift main.wasm 28
# wasmer JIT (singlepass)
$ time wasmer run --singlepass main.wasm 28
# wasmer 事前コンパイル (JIT+LLVM)
$ wasmer compile main.wasm -o main.wjit --jit --llvm
$ time wasmer main.wjit 28
# wasmer 事前コンパイル (JIT+cranelift)
$ wasmer compile main.wasm -o main-cranelift.wjit --jit --cranelift
$ time wasmer main-cranelift.wjit 28
# wasmer 事前コンパイル (JIT+singlepass)
$ wasmer compile main.wasm -o main-singlepass.wjit --jit --singlepass
$ time wasmer main-singlepass.wjit 28
# wasmer 事前コンパイル (native+LLVM)
$ wasmer compile main.wasm -o main.dylib --native --llvm
# (どれだけ待ってもコマンドが終わらない)
# wasmer 事前コンパイル (native+cranelift)
$ wasmer compile main.wasm -o main-cranelift.dylib --native --cranelift
$ time wasmer main-cranelift.dylib 28
# (表には載せていないがJIT+craneliftと同程度の実行時間)
# wasmer 事前コンパイル (native+singlepass)
$ wasmer compile main.wasm -o main-singlepass.dylib --native --singlepass
$ time wasmer main-singlepass.dylib 28
error: failed to run `main-singlepass.dylib`
│ 1: WASI execution failed
│ 2: failed to run WASI `_start` function
│ 3: RuntimeError: out of bounds memory access
╰─> 4: heap_get_oob
wasmの他言語への組み込み
作ったwasmをpythonで読み込んで使おうと試してみましたが,wasi_snapshot_preview1
のインポートが解決できないエラーが発生し,正常に実行できませんでした.
from wasmer import engine, Store, Module, Instance
from wasmer_compiler_cranelift import Compiler
store = Store(engine.JIT(Compiler))
module = Module(store, open('main.wasm', 'rb').read())
instance = Instance(module)
result = instance.exports.tetranacci(28)
print(result)
$ pip install wasmer
$ pip install wasmer_compiler_cranelift
$ python main.py
Traceback (most recent call last):
File "main.py", line 6, in <module>
instance = Instance(module)
RuntimeError: Error while importing "wasi_snapshot_preview1"."proc_exit": unknown import. Expected Function(FunctionType { params: [I32], results: [] })
swiftwasmが出力するwasmがおかしいのか,pipのwasmerがおかしいのか,原因はよくわかりませんでした・・・.
2021-02-12追記
作者の方からリプライしてもらい,wasi.Environment
を使ってインポートオブジェクトを構築することで,エントリポイントから実行することはできるようになりました.
With wasmer-python you need to setup WASI, https://t.co/9o1GkMMnUW. It’s not specified in the slides.
— 🦀 Ivan Enderlin 🐘🐍💎🐹☕🕸 (@mnt_io) February 11, 2021
cc @wasmerio
from wasmer import engine, wasi, Store, Module, Instance
from wasmer_compiler_cranelift import Compiler
store = Store(engine.JIT(Compiler))
module = Module(store, open('main.wasm', 'rb').read())
wasi_version = wasi.get_version(module, strict=True)
wasi_env = \
wasi.StateBuilder('wasi_test_program'). \
argument('10'). \
finalize()
import_object = wasi_env.generate_import_object(store, wasi_version)
instance = Instance(module, import_object)
instance.exports._start()
$ python main.py
29
ただ,instance.exports.tetranacci
を参照しようとするとLookupError: Export
tetranacci does not exist.
というエラーが出るので,エクスポートした関数が使えることまでは確認できませんでした.
refs
- WebAssembly
- WebAssembly - Wikipedia
- WebAssembly の概要 - WebAssembly | MDN
- A WebAssembly Compiler tale. How we abstracted our API to be… | by Syrus Akbary | Wasmer | Medium
- 【WebAssembly初心者必読】バイナリコードを使って「 WebAssembly 」の基礎を徹底解説してみた! | 株式会社ヌーラボ(Nulab inc.)
- SwiftWasm - compile Swift to WebAssembly
- WebAssemblyをWin/Mac/Linuxで実行可能なランタイム「Wasmer 1.0」正式リリース。事前コンパイルによる高速起動やクロスコンパイルなどにも対応 - Publickey
- wasmerio/wasmer: 🚀 The leading WebAssembly Runtime supporting WASI and Emscripten
- WebAssemblyをブラウザの外で動かすWasmerを触ってみた
- Compiling C to WebAssembly without Emscripten — surma.dev
- Node.jsでつくるNode.js-WASMコンパイラ - もくじ - Qiita
- Generalizations of Fibonacci numbers - Wikipedia