Web●●●○○

WebAssembly

うぇぶあせんぶり

ブラウザでネイティブ並みの速度で動くバイナリ形式。JSの補完としてWebの限界を広げる。

概要

WebAssembly(wasm)は、ブラウザ上でネイティブに近い速度で実行できる、コンパクトなバイナリ形式の実行フォーマットです。C・C++・Rust などで書いたプログラムをコンパイルして wasm バイナリを作り、それをブラウザの JavaScript エンジンが直接実行します。人間が書く言語ではなく、各種言語からのコンパイル先(コンパイルターゲット)として設計されている点が特徴です。

主要ブラウザすべてが対応する Web 標準であり、Figma(C++ 製の描画エンジン)、Google Meet の背景ぼかし、ブラウザ版 Photoshop、ゲームエンジンの Web 移植など、「JavaScript では性能が足りない」領域で実用されています。JavaScript を置き換えるものではなく、計算の重い部分を担う補完役 — これが WebAssembly の立ち位置です。

なぜ生まれたか

長らく「ブラウザで動く言語は JavaScript だけ」でした。動的型付けの JavaScript はエンジンの JIT コンパイラ(実行時に機械語へ変換する仕組み)の進化で高速になったものの、型が実行時に変わりうる言語の宿命として最適化には限界があり、画像処理・物理演算・ゲームのような計算集約的な処理では、ネイティブアプリとの性能差が埋まりませんでした。Flash などのプラグインで補う時代もありましたが、セキュリティと互換性の問題から廃れていきます。

転機は asm.js(2013年、Mozilla)でした。JavaScript のうち JIT が最適化しやすい書き方だけに制限したサブセットを定め、C/C++ から Emscripten というコンパイラでそこへ変換すると、ブラウザを変えずにネイティブの半分程度の速度が出る — この実証実験が「JavaScript を経由するのではなく、最初から実行効率のために設計されたバイナリ形式を標準化すればよい」という合意につながり、主要ブラウザベンダーの共同開発を経て 2017 年に WebAssembly が全ブラウザに搭載されました。テキストの JavaScript を解析するより、検証済みのバイナリを読み込むほうが、ダウンロードも起動も実行も速くできるのです。

詳細

実行モデル — スタックマシンと線形メモリ

wasm の実行モデルは意図的に単純です。命令は「値をスタックに積み、演算で取り出して結果を積み直す」スタックマシン方式で、型は整数と浮動小数点数の数値型が基本。この単純さのおかげで、エンジンはバイナリを高速に検証し、効率のよい機械語へ変換できます。メモリは「線形メモリ」と呼ばれる1本の連続したバイト列だけが与えられ、プログラムはその中のオフセット(先頭からの位置)でデータを読み書きします。C のポインタ演算がそのまま移植できる一方、線形メモリの外には一切アクセスできないため、ホスト側から見ると常にサンドボックス(隔離された砂場)の中で動くことになります。ネイティブ並みの速度と、ブラウザに要求される安全性を、この設計が同時に成立させています。

JavaScript との相互運用 — wasm は DOM を触れない

重要な制約として、wasm モジュールは DOM やネットワークなどのブラウザ機能を直接呼び出せません。できるのは数値計算と線形メモリの読み書き、そして「インポートされた関数の呼び出し」だけです。画面を更新したければ、JavaScript 側の関数をインポートして呼び出すか、計算結果を線形メモリに書いて JavaScript に読み取ってもらいます。つまり実際のアプリは「UI とグルー(接着)コードは JavaScript、重い計算は wasm」という分業になります。JavaScript と wasm の呼び出し境界には変換コストがあるため、細かく行き来するのではなく「大きな仕事をまとめて渡す」のが性能を出すコツです。

ブラウザJavaScript エンジンJavaScriptUI・グルーコードwasm重い計算相互呼び出し線形メモリDOM / Web API画面・通信などJS だけが直接アクセスできるwasm から直接は触れない
ブラウザ内での WebAssembly の位置 — JSと同じエンジン上で動くが、外界へはJS経由でしか出られない

どうやって wasm を作るか

wasm は原則として、既存の言語からコンパイルして作ります。C/C++ からは Emscripten、Rust からは wasm-pack などの公式に近いツールチェーンが整備されており、Go や C#(Blazor)も対応しています。既存の C/C++ 資産 — 画像コーデック、暗号ライブラリ、ゲームエンジン — を書き直さずに Web へ持ち込めることが、実務で wasm が選ばれる最大の理由です。なお、TypeScript を JavaScript に落とすトランスパイルが「ソースからソースへ」の変換であるのに対し、wasm へのコンパイルは低水準なバイナリへ落とす通常の意味でのコンパイルであり、変換の性質が異なる点は区別しておくとよいでしょう。

ブラウザの外へ — WASI と「軽量な実行単位」

wasm の「高速・安全・移植可能なサンドボックス」という性質は、ブラウザの外でも価値があります。ブラウザ外で動かすための標準インターフェースが WASI(WebAssembly System Interface)で、ファイルや時刻などの OS 機能への、権限を明示的に絞ったアクセス方法を定めています。ミリ秒未満で起動できる軽さから、CDN のエッジで動くサーバレス実行環境や、アプリケーションのプラグイン機構(ユーザーが書いたコードを安全に実行する仕組み)として採用が進んでいます。コンテナが「OS ごと隔離する」のに対し、wasm は「1つのモジュールを命令レベルで隔離する」より細かい単位であり、起動の速さと隔離の強さで勝る一方、既存アプリをそのまま動かす互換性ではコンテナに及びません。両者は置き換えではなく、隔離の粒度が異なる補完関係として使い分けられていく見込みです。