NimとCでもWebAssemblyやってみた

<http://qiita.com/akira_/items/55cf1f5911b14e265c6f>
こちらの記事に触発されてNim版とC版書いてみました。

# まず結果

`fibonacci(45)`の結果です

**環境**

```
CPU: Intel Core i7-6700
RAM: 8GB x2
WebBrowser: Google Chrome 57.0.2987.98 (64-bit)
```

| Language | Elapsed |
|:---------|--------:|
|JavaScript|19.176|
|C Wasm|4.357|
|Rust Wasm|4.354|
|Nim Wasm|4.359|

結果については、あくまで一回実行したものを貼っているだけで、実行環境と、実行するごとに結果が変わることをご了承ください。

# ソースコード

```nim
# Nim
import os
import times
import strutils

proc fibonacci*(n: int64): int64 =
  if n <= 1'i64:
    1'i64
  else:
    fibonacci(n - 1'i64) + fibonacci(n - 2'i64)

proc main() =
  let args = commandLineparams()
  let n = args[0].parseInt()
  echo "fibonacci($#) = $#" % [$n, $fibonacci(n.int64)]

main()
```

```c
// C
#include <stdint.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>

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

int main(int argc, char** argv) {
    int n = atoi(argv[1]);
    printf("fibonacci(%d) = %" PRId64 "\n", n, fibonacci(n));
    return 0;
}
```

# 手順

上記の記事ではDockerイメージを使ってビルドされたようですが、自分はVM上のXubuntuにemsdkを使ってインストールしたものをビルド環境として使用しました。

RustやNimなどの各種言語のコンパイラのインストールに関しては省略します。

### Emscriptenのインストール

<http://kripken.github.io/emscripten-site/docs/getting_started/downloads.html>
からemsdkをダウンロード、展開し、PATHを通します。

PATHを通したら

```sh
$ emsdk update
$ emsdk install sdk-incoming-64bit
$ emsdk activate sdk-incoming-64bit
```

でEmscripten本体のインストールと有効化ができます。

**追記**
この後に

```sh
$ source emsdk-portable/emsdk_env.sh
```
が必要なことを書き忘れていたので追記しておきます。
~/.profileなどに書いておくのがいいと思います。
**追記ここまで**

Emscriptenについては、`sdk-incoming-64bit`ならWebAssemblyのバージョンフラグがちゃんと1.0になっているようです。(2017/03/14時点)

ここで注意点としては、Emscriptenだけでなく、LLVMとClangのビルドが走るので、時間がかかるのとメモリを大量に消費します。
メモリに関してはスワップを用意するか、そもそも大量に用意しておくかで対処しましょう。(自分はVMに10GBのメモリを割り当てて対処しました)

あとは言語に合わせてコンパイラを走らせるだけです。

```sh
# C
$ emcc -O3 -s WASM=1 -o fib-c.js fibonacci.c
# Rust
$ rustc -O --target=wasm32-unknown-emscripten -o fib-rust.js fibonacci.rs
# Nim
$ nim c -d:release -d:emscripten -o:fib-nim.js fibonacci.nim
```

Nimの場合は以下のnim.cfgをソースと同ディレクトリに置いておきます。

```
@if emscripten:
  cc = clang
  clang.exe = "emcc"
  clang.linkerexe = "emcc"
  clang.options.linker = ""
  cpu = "i386"
  warning[GcMem]= off
  passC = "-s WASM=1"
  passL = "-s WASM=1"
@end
```

# 感想

- Emscriptenのビルドがただひたすらに重いのでつらい
    - Emscripten以外のto Wasmコンパイラの選択肢が欲しい気がする
- とりあえずWebAssembly使えば現代のコンパイラのおかげか結構高速に動いてくれるので良い
- 初回のEmscriptenを使ったコンパイルは時間がかかるが、2回目以降はどれも長くても5秒以内でコンパイルしてくれる
    - 特にNimはインクリメンタルコンパイルがあり、Rustも開発中なので大きいプログラムでも問題になることはなさそう

# まとめ
JavaScript、C、Rust、そしてNimの4つの言語のベンチマークをやりましたが、この記事はNimに興味持ってもらうために書いたようなものだったりします。現状WebAssemblyができる言語はC/C++/Rustしかないという認識が一般的なようですが、Nimも一応できるということを知ってもらえれば幸いです。(GCちゃんと動いてるかはっきりしてませんが・・・)
一応Nimでも`jsbind`とかを使えばDOM操作できたりします。

特にNimはGCありなのでC/C++/Rustよりも書きやすいと思っているので、WebAssemblyで手軽に高速化する言語としての選択肢になってほしいなとか勝手に思ったりしてます。

# 最後に

お手元のブラウザで手軽に試せるようにWebAssemblyのベンチマークができるサイトを作りました。

<https://snowlt23.github.io/wasm-bench-site/>

雑な作りではありますが、とりあえず動かしてなんとなく雰囲気を感じてもらえれば幸いです。

注意点として、雑な作りなのでWasmのパースからベンチマークしていて、それのせいでNが小さいとJavaScriptより遅くなってしまっています。 `40 <= N <= 46`ぐらいをおすすめします。

****

自分はJavaScriptを書くたびに心が削れていく人なのでWebで色んな言語が動くのが当たり前になる日がくるといいですね。