WebAssembly入門者向け情報をまとめてみる(標準化動向編)
Wasmは段階的に標準化が行われている。WebAsssembly Core Specification 1.0 (MVP) は2019年に標準化が完了しており、現在は2.0の仕様を策定中である。
ちなみに前提となる基礎知識はこちらで紹介している。
MVP仕様
WasmのMVP仕様はasm.js同等の機能をC/C++で使用できるという非常にシンプルなものである。
基本的な特徴としては以下のようなものがある。
- Wasmにおいて配布・ロード・実行可能なコードの単位はモジュールと呼ばれる
- 構造化されたスタックマシンとして動作する
- i32, i64, f32, f64の4つの型を持つ
- 64KiBの倍数の線形メモリ (ArrayBuffer) を持ち、上限は4GiBである
- コードは事前定義されたセクションごとに格納されてバイナリエンコーディングされる
- バイナリ化されたWasmファイルはテキスト形式に変換することで読み書きできる
- Webブラウザ以外の環境でも実行できる
- JavaScript VM (Node.js, etc.)、IoTデバイス、モバイル/デスクトップアプリ、など
2.0仕様
Wasm 2.0は2022/4/19に初めてドラフトが公開されており、非常にさまざまな機能をWasmに追加するという試みである。その中にはすでに主要4ブラウザで実装されている機能もあれば、提案されたばかりの機能もある。それぞれ重要な機能を書いていく。
ちなみに、以下のページでは2.0仕様が各ブラウザにおいてどこまで対応しているかがわかる。
2.0仕様はその状態によって6つのフェーズへと分けられている。https://github.com/WebAssembly/proposals
Threads and atomic (Phase 4)
Wasmの処理を複数スレッドで扱うために線形メモリを複数スレッドで共有し、排他制御できるようにする機能。Cならpthread、C++ならstd::threadなど言語によって標準で用意されているマルチスレッド機能を元にスレッド生成ができる。
Wasmがブラウザ上で動作する際、Web Workerを立ち上げて別スレッドを動作させる。この際、データのやり取りはSharedArrayBufferで共有することで実現する。
SharedArrayBufferは2017年にSpectre & Meltdownという脆弱性が発見されたため各ブラウザから排除され、クロスオリジン分離を行うことで有効化できるようになった。サイトにヘッダを加えることで有効にできるが、Safariが対応したのは結構最近なので注意。
対応バージョン
- Chrome: M74
- Firefox: 79
- Safari: 14.1 (クロスオリジン分離は15.2)
Fixed-width SIMD (Phase 5)
Native (というよりも各CPUアーキテクチャ) が一般的に使用しているSIMD命令に対応するための機能。MVPで対応しているi32, i64, f32, f64に加えて、固定長の並列処理を行うためのv128型 (128bit) が新たに追加された。
SIMDとは
Single Instruction Multiple Dataの名前の通り、単一の命令で複数の計算を同時に実行する方法。本来実行ユニットは1つの命令で1つの処理をするが、128bitのプロセッサであれば32bitの計算命令を同時に4つ処理できる。CPUアーキテクチャ毎に持っているSIMD命令セットに対して、並列計算したいデータを同時に渡す必要がある。
WasmにおけるSIMDでは以下の4種類に対応している。
- 64bit演算の2並列
- 32bit演算の4並列
- 16bit演算の8並列
- 8bit演算の16並列
Threadsよりも直近のバージョンでしか対応していないため、過去のバージョンでも動作するアプリを提供するのであれば、SIMDを使わない環境へフォールバックできるように作っておく必要がある。
対応バージョン
- Chrome: M91
- Firefox: 89
- Safari: 16.4
Relaxed SIMD (Phase 4)
SIMDをどこでも動作させるための厳格な規定を緩和して、ハードウェアの性能をより活用できるベクトル演算を追加するという提案。特定のプロセッサの上でより早く動かしたいというニーズのために用意されていると思われる。
対応バージョン
- Chrome: M114
- Firefox: 🚩 (Nightlyのみ)
- Safari: ❌
Reference Types (Phase 5)
externref/funcref
という2つの参照型を追加することで、JavaScript側のオブジェクトや関数をWasm側へ受け渡すことができる機能。これまで数の受け渡ししかできなかったWasmの課題が解消される。
例えば、JavaScript側で取得したDOMの参照をWasmへ渡してWasm側で処理を行うこともできる。
対応バージョン
- Chrome: M96
- Firefox: 79
- Safari: 15
Garbage collection (Phase 4)
JavaScriptがすでにGCを持っており、Wasm側にもGCを入れるとサイズが大きくなってしまうという課題があった。そのためJava、Kotlin、Dart、GoなどのGCを必要とする言語は導入しづらかった。
引用: https://youtu.be/RcHER-3gFXI?si=uf1RkACjoqGdpDfu
Wasm GCではJavaScriptとWasmがヒープを共有することで、一つのGCがまとめてメモリ管理をしてくれるようになる。
引用: https://youtu.be/RcHER-3gFXI?si=uf1RkACjoqGdpDfu
Wasm GCは従来のWasmとは別物と思ったほうが良いかもしれない。従来のWasmが新たなアーキテクチャへの移植をしているものだとすると、Wasm GCは新たなVMに移植するようなものである。LLVMも使用しないらしい。
また、決められた型に合わせる必要があるので、かなりの量新しいコードを書かなければならない。
従来よりも抽象度が上がるため、さまざまな最適化を行うことができ、例えばCやRustで生成したWasmよりもWasm GCの方がサイズを小さくできるらしい (malloc/freeのバンドル分)。
対応バージョン
- Chrome: M119
- Firefox: 120
- Safari: ❌
議論中の2.0仕様
その他、新しい提案が続々と追加されて検討が進んでいる。Phase 3に達すると実装が進められる段階となり、現在はエラーハンドリングや線形メモリ上限の拡張、JavaScript Promiseとの統合などの実装が進んでいる。