Edited at

WebAssemblyとJavaScriptのパフォーマンス比較(再帰・ループ)

More than 1 year has passed since last update.

Rustで書いたWebAssemblyでフィボナッチを計算したらJSより3倍速かった

上の記事でコメントをたくさんいただいたので、それぞれ実際に調べてみました。

ただ、結果に関する考察はしていませんのでご了承ください。(いつかしたい)

なお、RustおよびJavaScriptのソース、コンパイル後のファイルについてはGitHubで公開しています

また、今回はDockerで環境を作成したので、Dockerfileも公開します

(注1:ビルドには結構時間がかかります。私の環境で4時間ほどでした)

(注2:ビルドに必要なメモリは最低4GBです。DockerHubとの連携も試しましたが、ビルドの途中で落ちていたので諦めました)

(注3:イメージサイズが約26GBになります)


結果

wasm-js comparison.PNG

それぞれの計算内容は以下です。


fibo-**


  • フィボナッチ数列

fn fibonacci(n:i64) -> i64 {

if n <= 1 {
1
} else {
fibonacci(n - 1) + fibonacci(n - 2)
}
}


fibo-n-**


  • フィボナッチ数列(n <= 1のときにreturn n)

fn fibonacci(n:i64) -> i64 {

if n <= 1 {
n
} else {
fibonacci(n - 1) + fibonacci(n - 2)
}
}


fibo-asm-**


  • フィボナッチ数列(asm.js)


nthPrime-**


  • n番目の素数を求める


    • フィボナッチが再帰なのでループの場合も比較するため

    • フィボナッチのループはnを増やしても一瞬で終了してしまい、比較に適していなかったのでこちらにしました



fn nth_prime(n:i64) -> i64 {

let mut cnt = n;
let mut i = 3;
let mut primes = vec![2];
cnt -= 1;
while cnt > 0 {
if primes.iter().all(|&x| i % x != 0) {
primes.push(i);
cnt -= 1;
}
i += 2;
}
primes.pop().unwrap_or(-1)
}

なお、フィボナッチ系はn=46、素数はn=50000としました。


要点


  • ChromeにおいてはWebAssemblyのほうがJavaScriptよりも速かった(最大約3倍)

  • Firefoxにおいてはほぼ変わらないか、JavaScriptのほうが速かった

  • WebAssemblyはasm.jsより約2倍速かった

  • フィボナッチの計算で(return n if n <= 1)はWebAssemblyをChromeで実行した場合以外はあまり差がなかった


課題(疑問点)


  • なぜFirefoxではWebAssemblyとJavaScriptで結果がほとんど変わらなかったのか


    • 逆にJavaScriptのほうが速いケースがあったのはなぜか



  • フィボナッチの(return n if n <= 1)はなぜWebAssemblyをChromeで実行した場合のみ差が出たのか


まとめ

逆に疑問が増えたような気がしますが、一旦まとめました。

上の内容以外に気づいたこととして、rustc?Emscripten?の最適化の際に関数の結果がメモ化されているようです。

同じ引数での関数呼び出しをループしたときに処理時間がほとんどかかりませんでした。

まだまだ実用は難しいですが、RustもEmscriptenも日々更新されているので、新しい情報をキャッチしていきたいですね。


補足

各バージョン


  • emcc 1.37.9

  • rustc 1.18.0-nightly

  • Chrome 57.0.2987.133


    • #enable-asm-webassembly true

    • #enable-webassembly true



  • Firefox 52.0.2


    • javascript.options.asmjs true

    • javascript.options.throw_on_asmjs_validation_failure false

    • javascript.options.wasm true

    • javascript.options.wasm_baselinejit false