【WebAssembly】Wasmer で計算プログラムを実行するベンチマークをしてみた
Wasmer という WebAssembly (wasm) ランタイムが話題となり、wasm はブラウザ以外からも実行可能なバイナリフォーマットであるという認識が広まりつつあります。
Wasmer 上での動作がどの程度速いものか気になったので、ある計算プログラムを wasm にコンパイルしたものと macOS 向けにコンパイルしたものを用意してベンチマークしてみることにしました。
検証に用いる計算プログラム
グラフ理論的な計算プログラムである nauty-geng が C 言語で記述されているので、Emscripten で wasm にコンパイルして用いることにしました。
nauty-geng では与えられた条件を満たす同型ではない無向グラフを列挙したり、数え上げることができます。
動作確認環境
- MacBook Pro (16-inch, 2019)
- Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
- メモリ 16GB
- macOS Catalina
- Wasmer 1.0.0
- Emscripten 2.0.12
- nauty and Traces 2.7r1
事前準備
Wasmer の入手とインストール
curl https://get.wasmer.io -sSfL | sh
Emscripten の入手とインストール
nauty and Traces の入手と nauty-geng のコンパイル
nauty and Traces のサイトからソースコード(gzipped tar file)をダウンロードします:
curl -O https://pallini.di.uniroma1.it/nauty27r1.tar.gz
tar zxvf nauty27r1.tar.gz
cd nauty27r1/
makefile
のターゲット geng
に倣い、emcc
コマンドで nauty-geng を geng.wasm
としてコンパイルします:
(オプションに -DNAUTY_CPU_DEFINED -DCPUTIME=0.0
を指定する必要がありました[1])
$ emcc -o geng.wasm -O3 -DMAXN=WORDSIZE -DWORDSIZE=32 -DNAUTY_CPU_DEFINED -DCPUTIME=0.0 geng.c gtools.c nauty.c nautil.c naugraph.c schreier.c naurng.c
$ ls -l geng.wasm
-rwxr-xr-x 1 mascii staff 96147 1 15 22:20 geng.wasm*
比較のために makefile
を用いて gcc でもコンパイルしておきます:
$ make geng
$ file ./geng
./geng: Mach-O 64-bit executable x86_64
$ ls -l ./geng
-rwxr-xr-x 1 mascii staff 155412 1 15 22:20 ./geng*
コンパイルしたファイルの動作確認
geng.wasm
に渡すオプションは --
以降に記述します。
$ wasmer geng.wasm -- -uc 9 8
>A geng.wasm -cd1D8 n=9 e=8
>Z 47 graphs generated in 0.00 sec
$ ./geng -uc 9 8
>A ./geng -cd1D8 n=9 e=8
>Z 47 graphs generated in 0.00 sec
wasm にコンパイルしたものと macOS 向けにコンパイルしたものが同じように動作していることを確認できました。
なお、geng に渡した各オプションの意味は以下の通りです:
頂点の数が
ベンチマークしてみた
geng に渡すオプションは以下のものとし、それぞれ 10 回実行して実行時間を測定します。
-
-uc 20 19
(頂点の数が 20 の木の数え上げ) -
-ucd2D4 13
(頂点の数が 13 の最小次数: 2, 最大次数: 4 である連結グラフの数え上げ) -
-ucd3D3 20
(頂点の数が 20 の連結な立方体グラフの数え上げ)
-uc 20 19
1. ./geng -uc 20 19
wasmer geng.wasm -- -uc 20 19
wasmer run --llvm geng.wasm -- -uc 20 19
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | avg. | |
---|---|---|---|---|---|---|---|---|---|---|---|
native | 11.06s | 11.10s | 10.97s | 11.10s | 11.05s | 10.96s | 11.01s | 10.97s | 10.96s | 11.05s | 11.02s |
wasmer | 25.78s | 25.82s | 25.81s | 25.85s | 25.78s | 25.85s | 25.94s | 25.69s | 25.98s | 25.80s | 25.83s |
wasmer (llvm) | 12.71s | 12.52s | 12.59s | 12.54s | 12.67s | 12.52s | 12.62s | 12.53s | 12.57s | 12.72s | 12.60s |
(数え上げられたグラフの数: 823065)
LLVM による最適化を行うと、ネイティブとかなり近い速さになっていることがわかりました。
次のオプションからは LLVM による最適化を行った結果のみを測定します。
-ucd2D4 13
2. ./geng -ucd2D4 13
wasmer run --llvm geng.wasm -- -ucd2D4 13
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | avg. | |
---|---|---|---|---|---|---|---|---|---|---|---|
native | 57.78 | 57.64 | 57.24 | 57.39 | 57.24 | 57.46 | 57.44 | 57.39 | 57.38 | 57.39 | 57.44s |
wasmer (llvm) | 72.7 | 72.44 | 72.29 | 72.52 | 72.49 | 72.94 | 72.68 | 72.61 | 72.8 | 72.51 | 72.60s |
(数え上げられたグラフの数: 34734530)
wasmer はネイティブに比べ 1.25 倍程度の時間がかかりました。実行時間のばらつきは少ないようでした。
-ucd3D3 20
3. ./geng -ucd3D3 20
wasmer run --llvm geng.wasm -- -ucd3D3 20
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | avg. | |
---|---|---|---|---|---|---|---|---|---|---|---|
native | 219.43 | 219.5 | 216.98 | 217.65 | 216.24 | 215.27 | 216.6 | 217.8 | 218.65 | 218.81 | 217.69s |
wasmer (llvm) | 274.37 | 273.34 | 272.8 | 273.53 | 273.58 | 274.35 | 273.9 | 274.42 | 274.33 | 273.54 | 273.82s |
(数え上げられたグラフの数: 510489)
より計算に時間がかかるこのオプションでも wasmer はネイティブに比べ 1.25 倍程度の時間がかかりました。
ポータビリティも確かめてみた
実行時間だけでなく、実行環境に依存しないポータビリティについても確かめてみました。
Raspberry Pi
Raspberry Pi は ARM プロセッサを搭載していて、Raspberry Pi OS という Debian 系 Linux がよく用いられています。
Raspberry Pi 3 Model B に Raspberry Pi OS Lite をインストール後、Running Wasmer on a Raspberry Pi 4 with 64-bit userland を見て wasmer をインストールし、scp で wasm ファイルを転送して実行してみました:
(pi64)pi@raspberrypi:~ $ time wasmer geng.wasm -- -uc 20 19
>A geng.wasm -cd1D19 n=20 e=19
>Z 823065 graphs generated in 0.00 sec
real 1m23.641s
user 1m23.579s
sys 0m0.057s
LLVM による最適化を行おうとしましたが、エラーが出てしまいました:
(pi64)pi@raspberrypi:~ $ wasmer run --llvm geng.wasm -- -ucd2D4 13
error: failed to run `geng.wasm`
│ 1: module instantiation failed (engine: jit, compiler: llvm)
╰─> 2: Compilation error: unknown ELF relocation 283
Chrome
WebAssembly.sh という Web ターミナルに wasm ファイルをドラッグ&ドロップするだけで、WASI モジュールを実行できました。
まとめ
wasm はポータビリティを持ち合わせながらも、Wasmer で最適化して実行することで大きな性能の落ち込みがないことを確認できました。
これに加え、サンドボックス化された実行環境上で実行されることも wasm を利用するメリットなので、今後のエコシステムの発展が楽しみです。
Discussion