WebAssembly
WebAssemblyハンドブック
編集WebAssemblyとは
編集WebAssembly(略称:Wasm)は、モダンなウェブブラウザで動作する低レベルのバイナリフォーマットです。C++、Rust、C#などの言語からコンパイルされ、ネイティブに近いパフォーマンスを実現します。
主な特徴
編集- 高速実行
- 型安全
- メモリ安全
- プラットフォーム非依存
- 複数言語サポート
基本概念
編集モジュール
編集WebAssemblyのコードは「モジュール」として組織化されます。各モジュールには関数、メモリ、テーブルなどが含まれます。
基本的なモジュールの例(WAT形式):
(module (func $add (param $a i32) (param $b i32) (result i32) local.get $a local.get $b i32.add) (export "add" (func $add)))
- WAT形式
- WebAssembly Text Format
メモリモデル
編集WebAssemblyは線形メモリモデルを採用しています。メモリは連続したバイト配列として表現されます。
数値型
編集- i32: 32ビット整数
- i64: 64ビット整数
- f32: 32ビット浮動小数点
- f64: 64ビット浮動小数点
開発環境のセットアップ
編集必要なツール
編集- Emscripten: C/C++からWebAssemblyへのコンパイラ
- wasm-pack: RustのWebAssemblyビルドツール
- wat2wasm: テキスト形式からバイナリへの変換
インストール例(Unix系システム):
# Emscriptenのインストール git clone https://github.com/emscripten-core/emsdk.git cd emsdk ./emsdk install latest ./emsdk activate latest source ./emsdk_env.sh
基本的な使い方
編集C++からのコンパイル例
編集まず、簡単なC++プログラムを作成します:
- math.cpp
#include <emscripten/bind.h> int factorial(int n) { if (n <= 1) return 1; return n * factorial(n - 1); } EMSCRIPTEN_BINDINGS(math_module) { emscripten::function("factorial", &factorial); }
- コンパイルコマンド:
em++ math.cpp -o math.js -s WASM=1 -s EXPORTED_FUNCTIONS=['_factorial'] -s EXPORTED_RUNTIME_METHODS=['ccall','cwrap']
JavaScriptからの利用
編集// HTMLでの読み込み <script src="math.js"></script> <script> Module.onRuntimeInitialized = async () => { const result = Module.ccall( 'factorial', // 関数名 'number', // 戻り値の型 ['number'], // 引数の型 [5] // 引数の値 ); console.log(`5の階乗: ${result}`); // 出力: 120 }; </script>
メモリ管理
編集線形メモリの操作
編集WebAssemblyのメモリは、連続したバイト配列として表現されます:
// メモリの確保と操作 (module (memory 1) // 1ページ(64KB)のメモリを確保 (func $storeValue (param $addr i32) (param $value i32) local.get $addr local.get $value i32.store) (export "storeValue" (func $storeValue)))
JavaScriptとのメモリ共有
編集const memory = new WebAssembly.Memory({ initial: 1 }); const buffer = new Uint8Array(memory.buffer); // メモリへの書き込み buffer[0] = 42; // WebAssembly側での読み取り (func $readByte (param $addr i32) (result i32) local.get $addr i32.load8_u)
JavaScriptとの連携
編集関数のインポート
編集(module ;; JavaScriptの関数をインポート (import "console" "log" (func $log (param i32))) (func $doSomething i32.const 42 call $log) (export "doSomething" (func $doSomething)))
JavaScript側の実装
編集const importObject = { console: { log: function(value) { console.log(value); } } }; WebAssembly.instantiateStreaming(fetch('example.wasm'), importObject) .then(obj => { obj.instance.exports.doSomething(); });
実践的なユースケース
編集画像処理の例
編集グレースケール変換を行う簡単な例:
// C++ #include <emscripten/bind.h> #include <vector> void grayscale(std::vector<unsigned char>& pixels) { for (size_t i = 0; i < pixels.size(); i += 4) { unsigned char r = pixels[i]; unsigned char g = pixels[i + 1]; unsigned char b = pixels[i + 2]; unsigned char gray = (r + g + b) / 3; pixels[i] = gray; // R pixels[i + 1] = gray; // G pixels[i + 2] = gray; // B // Alpha値(pixels[i + 3])はそのまま } } EMSCRIPTEN_BINDINGS(image_module) { emscripten::function("grayscale", &grayscale); emscripten::register_vector<unsigned char>("Vector<unsigned char>"); }
パフォーマンスの最適化
編集最適化テクニック
編集- ループの最適化
- メモリアクセスパターンの改善
- SIMD命令の活用
SIMD例
編集// SIMDを使用した最適化例(WAT形式) (module (func $addVectors (param $a i32) (param $b i32) (param $len i32) (local $i i32) (local.set $i (i32.const 0)) (loop $loop ;; 4要素ずつ処理 (v128.store (local.get $a) (f32x4.add (v128.load (local.get $a)) (v128.load (local.get $b)))) (local.set $a (i32.add (local.get $a) (i32.const 16))) (local.set $b (i32.add (local.get $b) (i32.const 16))) (local.set $i (i32.add (local.get $i) (i32.const 4))) (br_if $loop (i32.lt_u (local.get $i) (local.get $len)))))
ツールとエコシステム
編集主要なツール
編集- wasm-bindgen: Rust-WebAssembly連携
- wabt: WebAssemblyバイナリツールキット
- Binaryen: コンパイラインフラストラクチャ
- wasm-pack: Rustパッケージング
- wasmer: スタンドアロンランタイム
デバッグツール
編集- Chrome DevTools
- Firefox WebAssembly デバッガー
- wat2wasm: テキスト形式の検証
パフォーマンス分析
編集- Chrome Performance Tools
- WebAssembly Studio
- Wasmtime
外部リンク
編集- WebAssembly 公式サイト
- MDN WebAssembly ドキュメント
- Emscripten ドキュメント
- Rust and WebAssembly Book