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

  • 15
    Like
  • 4
    Comment

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