WebAssemblyを利用してJavaScriptアプリケーションのパフォーマンスを向上させる方法

ウェブアプリケーションのパフォーマンス向上は、ユーザーエクスペリエンスの向上に直結します。そこで、本記事ではWebAssemblyを使用してJavaScriptアプリケーションの実行速度を向上させる方法を紹介します。

# 1. WebAssemblyとは
WebAssembly（通称Wasm）は、ウェブで高速な実行を実現するためコードです。。これは、ウェブブラウザ内で実行できる低レベルのバイナリフォーマットであり、JavaScriptの補完として設計されています。WebAssemblyの主な目的は、ウェブでの高性能なアプリケーションの実現を支援することです。これにより、C、C++、Rust、Goなどの言語で書かれたプログラムをウェブアプリケーションで直接実行することが可能になります。

Amazon Prime Videoで動画再生にWebAssemblyを採用されている実例があるようです。

https://www.publickey1.jp/blog/22/amazon_prime_videowebassemblywasm_vm.html

# 2. AssemblyScriptとは
AssemblyScriptは、TypeScriptに似た構文を持つ、WebAssembly向けの言語です。TypeScriptのような静的型付けやモダンな構文を提供しつつ、WebAssemblyの特性に最適化されたコンパイラを提供します。現在TypeScript勉強中のためTypeScriptで書いています。

# 3. AssemblyScriptのインストール方法
AssemblyScriptをインストールするには、Node.jsが必要です。以下のコマンドを使用して、AssemblyScriptをグローバルにインストールします。
node version 16以上で使えます。

```
npm install -g assemblyscript
```

インストールが完了したら、以下のコマンドでバージョンを確認できます。

```
asc --version

↓
※自分の場合
Version 0.27.24
```

# 4. WebAssemblyの実践的な利用例

WebAssemblyを使用しない場合とした場合で比較していきます。

## 4.1 WebAssemblyを使用しない場合
以下は、シンプルなフィボナッチ数列を計算するJavaScriptの例です。

```js
// JavaScriptのフィボナッチ関数
function fibonacciJS(n) {
    if (n <= 1) return n;
    let a = 0, b = 1;
    for (let i = 2; i <= n; i++) {
        let c = a + b;
        a = b;
        b = c;
    }
    return b;
}

console.time("javascript-fibonacci");
console.log(fibonacciJS(42));
console.timeEnd("javascript-fibonacci");
```

実行結果：
```terminal
※自分の場合
267914296
javascript-fibonacci: 2.371ms
```

上記のJavaScript関数を使用して、42番目のフィボナッチ数を計算する場合、実行時間は約2.4ミリ秒かかりました。

## 4.2 WebAssemblyを使用した場合
それに対して、同じ処理をWebAssemblyで記述した場合、実行時間が大幅に短縮されます。

以下は、AssemblyScriptを使用してWebAssemblyモジュールを作成し、フィボナッチ数列を計算する例です。

```typescript
// fibonacci.ts
export function fibonacci(n: i32): i32 {
    if (n <= 1) return n;
    let a = 0, b = 1;
    for (let i = 2; i <= n; i++) {
        let c = a + b;
        a = b;
        b = c;
    }
    return b;
}
```
i32はWASMでの型で「32bitの整数値（符号あり）」を意味しています。
参考：https://qiita.com/chikoski/items/d98ed0a98acfa896869f#%E8%BF%BD%E5%8A%A0%E3%81%95%E3%82%8C%E3%81%9F%E5%9E%8B
　　
　　
このAssemblyScriptコードをWebAssemblyにコンパイルするためには、次のコマンドを使用します。

```
asc fibonacci.ts -o fibonacci.wasm --optimize --validate
```

# 5. 実行時間の比較
JavaScriptとWebAssemblyでの実行時間を比較します。

```javascript
// index.js
const imports = { env: { abort: () => {} } };
const { fibonacci } = new WebAssembly.Instance(new WebAssembly.Module(new Uint8Array(require('fs').readFileSync('./fibonacci.wasm'))), imports).exports;

// JavaScriptのフィボナッチ関数
function fibonacciJS(n) {
    if (n <= 1) return n;
    let a = 0, b = 1;
    for (let i = 2; i <= n; i++) {
        let c = a + b;
        a = b;
        b = c;
    }
    return b;
}

// 計算するフィボナッチ数列の数
const n = 42;

// JavaScriptの実行時間計測
console.time("javascript-fibonacci");
console.log(fibonacciJS(n));
console.timeEnd("javascript-fibonacci");

// WebAssemblyの実行時間計測
console.time("wasm-fibonacci");
console.log(fibonacci(n));
console.timeEnd("wasm-fibonacci");
```

実行結果：
```terminal
※自分の場合
267914296
javascript-fibonacci: 2.411ms
267914296
wasm-fibonacci: 0.078ms
```

このコードでは、42番目のフィボナッチ数列を計算して、JavaScriptとWebAssemblyの場合とで同じアルゴリズムを実行してその実行時間を比較しています。
JavaScriptの実行時間が約2.4ミリ秒であるのに対し、WebAssemblyの実行時間は0.1ミリ秒以下程度になりました。
この例では2ミリ秒ほどの差になりました。やや差があるくらいですね。

通常、WebAssemblyの利点は、より複雑な計算や大量のデータ処理など、計算量が多いタスクで顕著に現れるようです。それらのタスクを実行する場合、WebAssemblyがJavaScriptよりも高速に処理を行うことが期待できます。しかし、今回のような単純な計算では、実行速度の差がほとんど感じられないことがあります。


# まとめ
WebAssemblyを利用することで、JavaScriptアプリケーションの実行速度を大幅に向上させることができます。特に、より複雑な計算や大量のデータ処理やパフォーマンスが重要な部分での利用が効果的です。

ただWebAssemblyでは対応ブラウザと対応言語の制限や下記のできないことがあります。

できないこと：
 - システムのルート権限でアクセスできない
 - DOM操作ができない

このため、利用機会が限られますが速度を出したいところでWebAssemblyを利用すると、うおー早い！となるはずです。

# 参考
[WebAssemblyとは？高速実行する最新技術のできる・できないこと、活用事例](https://www.techfirm.co.jp/blog/webassembly)
[AssemblyScriptを使ってTypeScriptのコードを早くしよう](https://qiita.com/chikoski/items/d98ed0a98acfa896869f)