WebAssembly
WebAssembly(Wasm)概要
1. はじめに
1.1 WebAssemblyとは
WebAssembly(略称: Wasm)は、スタックベースの仮想マシン向けに設計されたバイナリ命令フォーマットである。2017年にMozilla、Google、Microsoft、Appleの4大ブラウザベンダーによって共同で策定され、W3Cの公式標準として採択された。WebAssemblyの主な目的は、Webブラウザ上でネイティブに近いパフォーマンスでコードを実行することであり、C、C++、Rust、Goなどの言語からコンパイルされたコードをWeb上で動作させることを可能にする。
従来、Webブラウザで動作するプログラミング言語はJavaScriptのみであったが、WebAssemblyの登場により、開発者はより多くの言語の選択肢を持つことができるようになった。これにより、既存のコードベースをWeb上に移植したり、計算集約的なタスクをブラウザ内で高速に実行したりすることが可能になった。
1.2 WebAssemblyの歴史と背景
WebAssemblyの歴史は、Webパフォーマンスの向上に対する長年の取り組みに根ざしている。
asm.js(2013年)
Mozillaが開発したasm.jsは、JavaScriptのサブセットとして設計された低レベル言語である。JavaScriptエンジンが最適化しやすい形式でコードを記述することで、C/C++からコンパイルされたコードをブラウザ上で高速に実行することを目指した。asm.jsはWebAssemblyの直接的な前身であり、多くの概念がWebAssemblyに引き継がれている。
NaCl / PNaCl(2011年〜)
GoogleのNative Client(NaCl)およびPortable Native Client(PNaCl)は、ブラウザ内でネイティブコードを安全に実行するための技術であった。サンドボックス内でx86やARMのネイティブコードを直接実行する仕組みであったが、Chromeのみでのサポートに限定され、広範な採用には至らなかった。
WebAssemblyの策定(2015年〜2017年)
2015年にWebAssembly Community Groupが設立され、ブラウザベンダー間での共同開発が開始された。2017年3月にMVP(Minimum Viable Product)がリリースされ、主要4ブラウザ(Chrome、Firefox、Safari、Edge)すべてでサポートが開始された。
WebAssembly 2.0(2022年〜)
2022年以降、WebAssembly 2.0の策定が進行しており、SIMD(Single Instruction, Multiple Data)、参照型、例外処理、ガベージコレクション統合などの新機能が追加されている。
1.3 WebAssemblyの位置づけ
WebAssemblyは以下のような特徴を持つ技術として位置づけられる。
| 特徴 | 説明 |
|---|---|
| ポータブル | 特定のハードウェアやOSに依存しない |
| 安全 | サンドボックス内で実行され、メモリ安全性が保証される |
| 高速 | ネイティブコードに近い実行速度を実現 |
| オープン | W3C標準として公開仕様が管理されている |
| 言語非依存 | 複数のプログラミング言語からコンパイル可能 |
+--------------------------------------------------+
| Web Application |
+--------------------------------------------------+
| JavaScript | WebAssembly Module |
| (UI Logic) | (Computation-intensive) |
+--------------------------------------------------+
| Browser Runtime Engine |
| +------------------+ +---------------------+ |
| | JS Engine | | Wasm Runtime | |
| | (V8/Spider- | | (Sandboxed | |
| | Monkey/JSC) | | Execution) | |
| +------------------+ +---------------------+ |
+--------------------------------------------------+
| Operating System |
+--------------------------------------------------+
2. WebAssemblyのアーキテクチャ
2.1 スタックベース仮想マシン
WebAssemblyはスタックベースの仮想マシンとして設計されている。これは、命令がオペランドスタック上の値を操作する形式を意味する。レジスタベースの仮想マシン(例: Dalvik VM)と比較して、スタックベースの設計はバイナリフォーマットのサイズを小さくできるという利点がある。
;; スタックベースの加算例
;; WAT (WebAssembly Text Format)
(module
(func $add (param $a i32) (param $b i32) (result i32)
local.get $a ;; スタックに $a をプッシュ
local.get $b ;; スタックに $b をプッシュ
i32.add ;; スタックから2つの値をポップし、加算結果をプッシュ
)
(export "add" (func $add))
)
上記のWAT(WebAssembly Text Format)コードは、2つの32ビット整数を受け取り、その和を返す関数を定義している。実行の流れは以下の通りである。
スタックの状態変化:
1. local.get $a → スタック: [a]
2. local.get $b → スタック: [a, b]
3. i32.add → スタック: [a + b]
2.2 型システム
WebAssemblyは、静的型付けの型システムを採用している。MVPでサポートされている基本型は以下の4つである。
| 型 | サイズ | 説明 |
|---|---|---|
i32 | 32ビット | 32ビット整数 |
i64 | 64ビット | 64ビット整数 |
f32 | 32ビット | 単精度浮動小数点数(IEEE 754) |
f64 | 64ビット | 倍精度浮動小数点数(IEEE 754) |
WebAssembly 2.0以降では、以下の型も追加されている。
| 型 | 説明 |
|---|---|
v128 | 128ビットSIMDベクトル型 |
funcref | 関数参照型 |
externref | 外部参照型(ホスト環境のオブジェクトへの参照) |
;; 型の使用例
(module
;; 整数型の使用
(func $integer_ops (param $x i32) (param $y i64) (result i64)
local.get $x
i64.extend_i32_s ;; i32 → i64 への符号拡張変換
local.get $y
i64.add
)
;; 浮動小数点型の使用
(func $float_ops (param $x f32) (param $y f64) (result f64)
local.get $x
f64.promote_f32 ;; f32 → f64 への昇格変換
local.get $y
f64.mul
)
(export "integer_ops" (func $integer_ops))
(export "float_ops" (func $float_ops))
)
2.3 メモリモデル
WebAssemblyのメモリは、線形メモリ(Linear Memory)と呼ばれるバイト配列として管理される。この線形メモリはページ単位(1ページ = 64KB)で割り当てられ、memory.grow命令によって動的に拡張できる。
;; メモリの定義と操作
(module
;; 初期1ページ(64KB)、最大10ページ(640KB)のメモリを定義
(memory (export "memory") 1 10)
;; メモリへの書き込み
(func $store_value (param $offset i32) (param $value i32)
local.get $offset
local.get $value
i32.store ;; メモリの指定オフセットに値を格納
)
;; メモリからの読み取り
(func $load_value (param $offset i32) (result i32)
local.get $offset
i32.load ;; メモリの指定オフセットから値を読み取り
)
;; メモリの拡張
(func $grow_memory (param $pages i32) (result i32)
local.get $pages
memory.grow ;; メモリを指定ページ数分拡張(以前のページ数を返す)
)
(export "store_value" (func $store_value))
(export "load_value" (func $load_value))
(export "grow_memory" (func $grow_memory))
)
メモリモデルの構造を図示すると以下のようになる。
WebAssembly Linear Memory
+------------------------------------------------------------------+
| Page 0 (0x00000 - 0x0FFFF) | Page 1 (0x10000 - 0x1FFFF) | ... |
| 64KB | 64KB | |
+------------------------------------------------------------------+
| ↑ |
| offset 0 |
| |
| Byte-addressable, bounds-checked |
+------------------------------------------------------------------+
アクセスパターン:
- i32.load offset=0 → 4バイトを読み取り (offset 0-3)
- i32.store offset=4 → 4バイトを書き込み (offset 4-7)
- i32.load8_s offset=8 → 1バイトを読み取り、符号拡張
2.4 テーブルと間接呼び出し
WebAssemblyのテーブルは、関数参照の配列であり、間接的な関数呼び出しを可能にする。これは、関数ポインタや仮想関数テーブル(vtable)の実装に使用される。
;; テーブルと間接呼び出しの例
(module
;; 関数型の定義
(type $binary_op (func (param i32 i32) (result i32)))
;; テーブルの定義(最小2要素)
(table 2 funcref)
;; 関数の定義
(func $add (param i32 i32) (result i32)
local.get 0
local.get 1
i32.add
)
(func $mul (param i32 i32) (result i32)
local.get 0
local.get 1
i32.mul
)
;; テーブルに関数を登録
(elem (i32.const 0) $add $mul)
;; 間接呼び出し
(func $call_op (param $op_index i32) (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
local.get $op_index
call_indirect (type $binary_op) ;; テーブルのインデックスで関数を呼び出し
)
(export "call_op" (func $call_op))
)
// JavaScript側からの使用例
const instance = await WebAssembly.instantiate(wasmModule);
const { call_op } = instance.exports;
console.log(call_op(0, 3, 4)); // add(3, 4) = 7
console.log(call_op(1, 3, 4)); // mul(3, 4) = 12
3. WebAssemblyのバイナリフォーマット
3.1 モジュール構造
WebAssemblyのバイナリフォーマット(.wasmファイル)は、明確に定義されたセクション構造を持つ。各セクションは特定の種類の情報を格納し、セクションIDによって識別される。
WebAssembly バイナリフォーマット構造
+--------------------------------------------------+
| Magic Number: 0x00 0x61 0x73 0x6D ("\0asm") |
| Version: 0x01 0x00 0x00 0x00 (version 1) |
+--------------------------------------------------+
| Section 1: Type Section (ID: 1) |
| - 関数シグネチャの定義 |
+--------------------------------------------------+
| Section 2: Import Section (ID: 2) |
| - 外部モジュールからのインポート宣言 |
+--------------------------------------------------+
| Section 3: Function Section (ID: 3) |
| - 関数宣言(型インデックスの参照) |
+--------------------------------------------------+
| Section 4: Table Section (ID: 4) |
| - テーブルの定義 |
+--------------------------------------------------+
| Section 5: Memory Section (ID: 5) |
| - 線形メモリの定義 |
+--------------------------------------------------+
| Section 6: Global Section (ID: 6) |
| - グローバル変数の定義 |
+--------------------------------------------------+
| Section 7: Export Section (ID: 7) |
| - エクスポート宣言 |
+--------------------------------------------------+
| Section 8: Start Section (ID: 8) |
| - エントリポイント関数の指定 |
+--------------------------------------------------+
| Section 9: Element Section (ID: 9) |
| - テーブル初期化データ |
+--------------------------------------------------+
| Section 10: Code Section (ID: 10) |
| - 関数本体(命令列) |
+--------------------------------------------------+
| Section 11: Data Section (ID: 11) |
| - メモリ初期化データ |
+--------------------------------------------------+
| Section 0: Custom Sections (ID: 0) |
| - デバッグ情報、名前セクションなど |
+--------------------------------------------------+
3.2 バイナリエンコーディング
WebAssemblyのバイナリフォーマットは、LEB128(Little Endian Base 128)エンコーディングを使用して整数値を効率的にエンコードする。
# LEB128エンコーディングの例
def encode_unsigned_leb128(value):
"""符号なしLEB128エンコーディング"""
result = []
while True:
byte = value & 0x7F # 下位7ビットを取得
value >>= 7
if value != 0:
byte |= 0x80 # 継続ビットを設定
result.append(byte)
if value == 0:
break
return bytes(result)
def encode_signed_leb128(value):
"""符号付きLEB128エンコーディング"""
result = []
more = True
while more:
byte = value & 0x7F
value >>= 7
if (value == 0 and (byte & 0x40) == 0) or \
(value == -1 and (byte & 0x40) != 0):
more = False
else:
byte |= 0x80
result.append(byte)
return bytes(result)
# 使用例
print(encode_unsigned_leb128(624485).hex()) # e5 8e 26
print(encode_signed_leb128(-123456).hex()) # c0 bb 78
3.3 テキストフォーマット(WAT)
WATはWebAssemblyのテキスト表現であり、S式(S-expression)ベースの構文を使用する。開発者はWATを使用してWebAssemblyモジュールを手動で記述したり、デバッグ目的で使用したりすることができる。
;; 完全なWATモジュールの例:フィボナッチ数列
(module
;; メモリの定義
(memory (export "memory") 1)
;; フィボナッチ数列を再帰的に計算
(func $fib_recursive (param $n i32) (result i32)
;; ベースケース: n <= 1 の場合、n を返す
(if (result i32) (i32.le_s (local.get $n) (i32.const 1))
(then (local.get $n))
(else
;; fib(n-1) + fib(n-2)
(i32.add
(call $fib_recursive (i32.sub (local.get $n) (i32.const 1)))
(call $fib_recursive (i32.sub (local.get $n) (i32.const 2)))
)
)
)
)
;; フィボナッチ数列を反復的に計算(効率的な実装)
(func $fib_iterative (param $n i32) (result i32)
(local $a i32)
(local $b i32)
(local $temp i32)
(local $i i32)
(local.set $a (i32.const 0))
(local.set $b (i32.const 1))
(local.set $i (i32.const 0))
(block $break
(loop $loop
(br_if $break (i32.ge_s (local.get $i) (local.get $n)))
(local.set $temp (local.get $b))
(local.set $b (i32.add (local.get $a) (local.get $b)))
(local.set $a (local.get $temp))
(local.set $i (i32.add (local.get $i) (i32.const 1)))
(br $loop)
)
)
(local.get $a)
)
(export "fib_recursive" (func $fib_recursive))
(export "fib_iterative" (func $fib_iterative))
)
3.4 WATからWasmへの変換
WAT(テキスト形式)とWasm(バイナリ形式)の間の変換には、WebAssembly Binary Toolkit(WABT)を使用する。
# WABTのインストール
# macOS
brew install wabt
# Ubuntu/Debian
sudo apt-get install wabt
# WAT → Wasm (テキスト → バイナリ)
wat2wasm fibonacci.wat -o fibonacci.wasm
# Wasm → WAT (バイナリ → テキスト)
wasm2wat fibonacci.wasm -o fibonacci.wat
# Wasmの情報を表示
wasm-objdump -h fibonacci.wasm # ヘッダ情報
wasm-objdump -d fibonacci.wasm # 逆アセンブル
wasm-objdump -x fibonacci.wasm # セクション詳細
# バリデーション
wasm-validate fibonacci.wasm
4. WebAssemblyの実行環境
4.1 ブラウザ環境でのWebAssembly
すべての主要ブラウザがWebAssemblyをネイティブにサポートしている。JavaScriptのWebAssembly APIを通じてWasmモジュールのロード、コンパイル、インスタンス化を行う。
// 方法1: WebAssembly.instantiateStreaming(推奨)
// HTTPレスポンスから直接ストリーミングコンパイル
async function loadWasmStreaming() {
const response = await fetch('fibonacci.wasm');
const { instance, module } = await WebAssembly.instantiateStreaming(
response,
{
// インポートオブジェクト
env: {
// Wasmモジュールがインポートする関数を提供
log: (value) => console.log('Wasm says:', value),
memory: new WebAssembly.Memory({ initial: 1, maximum: 10 }),
},
}
);
// エクスポートされた関数を呼び出し
console.log(instance.exports.fib_iterative(10)); // 55
return { instance, module };
}
// 方法2: WebAssembly.instantiate(ArrayBuffer経由)
async function loadWasmBuffer() {
const response = await fetch('fibonacci.wasm');
const bytes = await response.arrayBuffer();
const { instance, module } = await WebAssembly.instantiate(bytes);
console.log(instance.exports.fib_iterative(10)); // 55
return { instance, module };
}
// 方法3: コンパイルとインスタンス化を分離
async function loadWasmSeparate() {
const response = await fetch('fibonacci.wasm');
const bytes = await response.arrayBuffer();
// コンパイル(Module作成)
const module = await WebAssembly.compile(bytes);
// モジュール情報の取得
console.log('Imports:', WebAssembly.Module.imports(module));
console.log('Exports:', WebAssembly.Module.exports(module));
// インスタンス化
const instance = await WebAssembly.Instance(module);
return instance;
}
// 方法4: 同期的なインスタンス化(小さなモジュール向け)
function loadWasmSync(bytes) {
const module = new WebAssembly.Module(bytes);
const instance = new WebAssembly.Instance(module);
return instance;
}
4.2 メモリの共有とデータ交換
JavaScriptとWebAssembly間のデータ交換は、共有メモリ(SharedArrayBuffer)またはWebAssemblyの線形メモリを通じて行われる。
// JavaScript ↔ WebAssembly メモリ共有の例
async function memoryExample() {
// メモリの作成
const memory = new WebAssembly.Memory({
initial: 1, // 1ページ = 64KB
maximum: 10, // 最大10ページ = 640KB
});
// WATモジュール(インライン定義の例)
const watSource = `
(module
(import "env" "memory" (memory 1 10))
;; 文字列をメモリに書き込む関数
(func $write_string (param $offset i32) (param $char i32)
local.get $offset
local.get $char
i32.store8
)
;; メモリ上の配列の合計を計算
(func $sum_array (param $offset i32) (param $length i32) (result i32)
(local $sum i32)
(local $i i32)
(local.set $sum (i32.const 0))
(local.set $i (i32.const 0))
(block $break
(loop $loop
(br_if $break (i32.ge_u (local.get $i) (local.get $length)))
(local.set $sum
(i32.add
(local.get $sum)
(i32.load
(i32.add
(local.get $offset)
(i32.mul (local.get $i) (i32.const 4))
)
)
)
)
(local.set $i (i32.add (local.get $i) (i32.const 1)))
(br $loop)
)
)
(local.get $sum)
)
(export "write_string" (func $write_string))
(export "sum_array" (func $sum_array))
)
`;
const instance = await WebAssembly.instantiate(
wasmModule,
{ env: { memory } }
);
// JavaScript側からメモリに配列を書き込む
const view = new Int32Array(memory.buffer);
view[0] = 10;
view[1] = 20;
view[2] = 30;
view[3] = 40;
view[4] = 50;
// WebAssembly側で合計を計算
const sum = instance.exports.sum_array(0, 5);
console.log('Sum:', sum); // 150
// 文字列の受け渡し
const encoder = new TextEncoder();
const decoder = new TextDecoder();
// JavaScriptからWebAssemblyメモリに文字列を書き込む
const str = "Hello, WebAssembly!";
const encoded = encoder.encode(str);
const uint8View = new Uint8Array(memory.buffer);
uint8View.set(encoded, 1024); // オフセット1024から書き込み
// メモリから文字列を読み取る
const readStr = decoder.decode(uint8View.slice(1024, 1024 + encoded.length));
console.log('String:', readStr); // "Hello, WebAssembly!"
}
4.3 サーバーサイドWebAssembly
WebAssemblyはブラウザ以外の環境でも実行可能である。主要なサーバーサイドランタイムには以下のものがある。
Wasmtime
Bytecode Allianceが開発するスタンドアロンのWebAssemblyランタイムで、WASI(WebAssembly System Interface)をサポートする。
# Wasmtimeのインストール
curl https://wasmtime.dev/install.sh -sSf | bash
# Wasmモジュールの実行
wasmtime run hello.wasm
# WASIを使用するモジュールの実行
wasmtime run --dir=. app.wasm
# 事前コンパイル(AOT)
wasmtime compile app.wasm -o app.cwasm
wasmtime run app.cwasm
Wasmer
高性能なWebAssemblyランタイムで、複数のコンパイルバックエンド(Singlepass、Cranelift、LLVM)をサポートする。
# Wasmerのインストール
curl https://get.wasmer.io -sSfL | sh
# Wasmモジュールの実行
wasmer run hello.wasm
# パッケージの実行(WAPM経由)
wasmer run python/python
# コンパイルバックエンドの指定
wasmer run --compiler=cranelift app.wasm
wasmer run --compiler=llvm app.wasm # 最適化重視
wasmer run --compiler=singlepass app.wasm # コンパイル速度重視
WasmEdge
CNCF(Cloud Native Computing Foundation)のサンドボックスプロジェクトとして管理される軽量ランタイムで、エッジコンピューティングやサーバーレスに適している。
# WasmEdgeのインストール
curl -sSf https://raw.githubusercontent.com/WasmEdge/WasmEdge/master/utils/install.sh | bash
# Wasmモジュールの実行
wasmedge run hello.wasm
# AOTコンパイル
wasmedgec app.wasm app_aot.wasm
wasmedge run app_aot.wasm
5. WASI(WebAssembly System Interface)
5.1 WASIの概要
WASI(WebAssembly System Interface)は、WebAssemblyモジュールがオペレーティングシステムのリソースにアクセスするための標準化されたインターフェースである。WASIはPOSIXライクなAPIを提供し、ファイルシステム、ネットワーク、クロック、乱数生成などのシステム機能へのアクセスを可能にする。
WASIの設計原則は「Capability-based Security(ケーパビリティベースセキュリティ)」に基づいている。これは、プログラムがアクセスできるリソースを明示的に制限する仕組みであり、従来のアンビエント権限モデル(プロセスがユーザーのすべての権限を継承する)よりも安全である。
WASIのアーキテクチャ
+---------------------------------------------------+
| WebAssembly Module |
| +-------------------------------------------+ |
| | Application Code | |
| +-------------------------------------------+ |
| | WASI API Calls | |
| | fd_read, fd_write, path_open, etc. | |
| +-------------------------------------------+ |
+---------------------------------------------------+
↓
+---------------------------------------------------+
| WASI Runtime Layer |
| +-------------------------------------------+ |
| | Capability-based Security Check | |
| | (Pre-opened file descriptors, | |
| | allowed directories, etc.) | |
| +-------------------------------------------+ |
| | OS Abstraction Layer | |
| +-------------------------------------------+ |
+---------------------------------------------------+
↓
+---------------------------------------------------+
| Operating System |
| File System | Network | Clock | Random | ... |
+---------------------------------------------------+
5.2 WASI Preview 1 API
WASI Preview 1は、最初の安定版WASIインターフェースであり、以下のモジュールを提供する。
// WASI Preview 1の主要関数(Rust擬似コード)
// ファイルシステム操作
wasi_snapshot_preview1::fd_read(fd, iovs, nread) // ファイルから読み取り
wasi_snapshot_preview1::fd_write(fd, iovs, nwritten) // ファイルに書き込み
wasi_snapshot_preview1::fd_seek(fd, offset, whence, newoffset) // シーク
wasi_snapshot_preview1::fd_close(fd) // ファイルディスクリプタを閉じる
wasi_snapshot_preview1::path_open(dirfd, path, oflags, fd) // パスを開く
wasi_snapshot_preview1::path_create_directory(fd, path) // ディレクトリ作成
wasi_snapshot_preview1::path_remove_directory(fd, path) // ディレクトリ削除
wasi_snapshot_preview1::path_rename(old_fd, old_path, new_fd, new_path) // リネーム
// 環境情報
wasi_snapshot_preview1::args_get(argv, argv_buf) // コマンドライン引数取得
wasi_snapshot_preview1::args_sizes_get(argc, argv_buf_size)
wasi_snapshot_preview1::environ_get(environ, environ_buf) // 環境変数取得
wasi_snapshot_preview1::environ_sizes_get(environc, environ_buf_size)
// クロック
wasi_snapshot_preview1::clock_time_get(clock_id, precision, time)
wasi_snapshot_preview1::clock_res_get(clock_id, resolution)
// プロセス
wasi_snapshot_preview1::proc_exit(exit_code) // プロセス終了
// 乱数
wasi_snapshot_preview1::random_get(buf, buf_len) // 乱数生成
5.3 WASI Preview 2とコンポーネントモデル
WASI Preview 2は、コンポーネントモデルに基づく新しいアーキテクチャを導入している。コンポーネントモデルは、WebAssemblyモジュール間の相互運用性を大幅に改善し、WIT(WebAssembly Interface Types)を使用してインターフェースを定義する。
// WIT (WebAssembly Interface Types) の定義例
// ファイル: my-component.wit
package my-org:my-component@1.0.0;
// インターフェースの定義
interface types {
// レコード型(構造体に相当)
record user {
id: u64,
name: string,
email: string,
active: bool,
}
// バリアント型(列挙型に相当)
variant error {
not-found(string),
permission-denied,
internal(string),
}
// 結果型
type result-user = result<user, error>;
}
// ワールドの定義(コンポーネントのインターフェース全体)
world my-world {
// インポート(外部から提供される機能)
import wasi:io/streams@0.2.0;
import wasi:filesystem/types@0.2.0;
// エクスポート(このコンポーネントが提供する機能)
export get-user: func(id: u64) -> result<types.user, types.error>;
export list-users: func() -> list<types.user>;
export create-user: func(name: string, email: string) -> result<types.user, types.error>;
}
# コンポーネントモデルのツールチェーン
# wasm-toolsのインストール
cargo install wasm-tools
# コアモジュールをコンポーネントに変換
wasm-tools component new core-module.wasm \
--adapt wasi_snapshot_preview1.reactor.wasm \
-o component.wasm
# コンポーネントのWIT情報を表示
wasm-tools component wit component.wasm
# コンポーネントの結合(Composition)
wasm-tools compose component-a.wasm \
--definitions component-b.wasm \
-o composed.wasm
5.4 WASIを使用したプログラミング(Rust例)
// Cargo.toml
// [dependencies]
// # WASI Preview 1対応
// src/main.rs
use std::fs;
use std::io::{self, Read, Write};
use std::env;
fn main() -> io::Result<()> {
// コマンドライン引数の取得
let args: Vec<String> = env::args().collect();
println!("Arguments: {:?}", args);
// 環境変数の取得
for (key, value) in env::vars() {
println!("{}={}", key, value);
}
// ファイルの読み書き(WASIランタイムが許可したディレクトリのみ)
// ファイルへの書き込み
let mut file = fs::File::create("output.txt")?;
file.write_all(b"Hello from WebAssembly with WASI!\n")?;
// ファイルからの読み取り
let content = fs::read_to_string("output.txt")?;
println!("File content: {}", content);
// 標準入出力
print!("Enter your name: ");
io::stdout().flush()?;
let mut name = String::new();
io::stdin().read_line(&mut name)?;
println!("Hello, {}!", name.trim());
// 現在時刻の取得
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap();
println!("Current timestamp: {} seconds", now.as_secs());
Ok(())
}
# RustでWASI対応Wasmをビルド
# WASIターゲットの追加
rustup target add wasm32-wasip1
# ビルド
cargo build --target wasm32-wasip1 --release
# Wasmtimeで実行(ファイルシステムアクセスを許可)
wasmtime run \
--dir=. \
--env "MY_VAR=hello" \
target/wasm32-wasip1/release/my_app.wasm -- arg1 arg2
6. 各言語からのWebAssemblyコンパイル
6.1 C/C++(Emscripten)
EmscriptenはC/C++コードをWebAssemblyにコンパイルするための最も成熟したツールチェーンである。
// hello.c - C言語のWebAssembly例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <emscripten/emscripten.h>
// JavaScriptにエクスポートする関数
EMSCRIPTEN_KEEPALIVE
int fibonacci(int n) {
if (n <= 1) return n;
int a = 0, b = 1;
for (int i = 2; i <= n; i++) {
int temp = b;
b = a + b;
a = temp;
}
return b;
}
// 画像処理の例:グレースケール変換
EMSCRIPTEN_KEEPALIVE
void grayscale(uint8_t* pixels, int width, int height) {
for (int i = 0; i < width * height * 4; i += 4) {
// RGBA → グレースケール
uint8_t gray = (uint8_t)(
0.299 * pixels[i] + // R
0.587 * pixels[i + 1] + // G
0.114 * pixels[i + 2] // B
);
pixels[i] = gray;
pixels[i + 1] = gray;
pixels[i + 2] = gray;
// pixels[i + 3] はアルファ値(変更なし)
}
}
// 行列乗算の例
EMSCRIPTEN_KEEPALIVE
void matrix_multiply(float* A, float* B, float* C, int M, int N, int K) {
for (int i = 0; i < M; i++) {
for (int j = 0; j < N; j++) {
float sum = 0.0f;
for (int k = 0; k < K; k++) {
sum += A[i * K + k] * B[k * N + j];
}
C[i * N + j] = sum;
}
}
}
int main() {
printf("WebAssembly C Module Loaded\n");
printf("fib(10) = %d\n", fibonacci(10));
return 0;
}
# Emscriptenのインストール
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh
# コンパイル(基本)
emcc hello.c -o hello.wasm
# コンパイル(JavaScriptグルーコード付き)
emcc hello.c -o hello.js \
-s EXPORTED_FUNCTIONS='["_fibonacci", "_grayscale", "_matrix_multiply", "_malloc", "_free"]' \
-s EXPORTED_RUNTIME_METHODS='["ccall", "cwrap", "setValue", "getValue"]' \
-s ALLOW_MEMORY_GROWTH=1 \
-s INITIAL_MEMORY=16777216 \
-O3
# コンパイル(HTML出力)
emcc hello.c -o hello.html \
-s EXPORTED_FUNCTIONS='["_fibonacci"]' \
-O3
# WASI対応ビルド(スタンドアロン)
emcc hello.c -o hello.wasm \
-s STANDALONE_WASM=1 \
-s PURE_WASI=1
// Emscriptenでコンパイルしたモジュールの使用
// hello.jsが自動生成するModule APIを使用
Module.onRuntimeInitialized = function() {
// ccallを使用(直接呼び出し)
const result = Module.ccall(
'fibonacci', // 関数名
'number', // 戻り値の型
['number'], // 引数の型
[10] // 引数の値
);
console.log('fib(10) =', result); // 55
// cwrapを使用(関数をラップ)
const fib = Module.cwrap('fibonacci', 'number', ['number']);
console.log('fib(20) =', fib(20)); // 6765
// メモリ操作を伴う関数の呼び出し(画像処理)
const width = 100, height = 100;
const size = width * height * 4;
// Wasmメモリにバッファを確保
const ptr = Module._malloc(size);
// JavaScriptのImageDataからWasmメモリにコピー
const imageData = ctx.getImageData(0, 0, width, height);
Module.HEAPU8.set(imageData.data, ptr);
// グレースケール変換を実行
Module._grayscale(ptr, width, height);
// 結果をJavaScriptに読み戻し
imageData.data.set(Module.HEAPU8.subarray(ptr, ptr + size));
ctx.putImageData(imageData, 0, 0);
// メモリの解放
Module._free(ptr);
};
6.2 Rust
Rustは、メモリ安全性とゼロコスト抽象化を特徴とする言語であり、WebAssemblyのターゲットとして非常に適している。
// Cargo.toml
// [package]
// name = "wasm-example"
// version = "0.1.0"
// edition = "2021"
//
// [lib]
// crate-type = ["cdylib"]
//
// [dependencies]
// wasm-bindgen = "0.2"
// js-sys = "0.3"
// web-sys = { version = "0.3", features = [
// "console", "Document", "Element", "HtmlElement",
// "HtmlCanvasElement", "CanvasRenderingContext2d",
// "Window", "Performance",
// ]}
use wasm_bindgen::prelude::*;
use web_sys::console;
// JavaScriptにエクスポートする関数
#[wasm_bindgen]
pub fn greet(name: &str) -> String {
format!("Hello, {}! From Rust WebAssembly", name)
}
// 構造体のエクスポート
#[wasm_bindgen]
pub struct Universe {
width: u32,
height: u32,
cells: Vec<u8>,
}
#[wasm_bindgen]
impl Universe {
#[wasm_bindgen(constructor)]
pub fn new(width: u32, height: u32) -> Universe {
let cells = vec![0; (width * height) as usize];
Universe { width, height, cells }
}
pub fn width(&self) -> u32 {
self.width
}
pub fn height(&self) -> u32 {
self.height
}
pub fn cells_ptr(&self) -> *const u8 {
self.cells.as_ptr()
}
// Conway's Game of Life のティック
pub fn tick(&mut self) {
let mut next = self.cells.clone();
for row in 0..self.height {
for col in 0..self.width {
let idx = self.get_index(row, col);
let cell = self.cells[idx];
let live_neighbors = self.live_neighbor_count(row, col);
let next_cell = match (cell, live_neighbors) {
(1, x) if x < 2 => 0, // 過疎
(1, 2) | (1, 3) => 1, // 生存
(1, x) if x > 3 => 0, // 過密
(0, 3) => 1, // 誕生
(otherwise, _) => otherwise,
};
next[idx] = next_cell;
}
}
self.cells = next;
}
fn get_index(&self, row: u32, column: u32) -> usize {
(row * self.width + column) as usize
}
fn live_neighbor_count(&self, row: u32, column: u32) -> u8 {
let mut count = 0;
for delta_row in [self.height - 1, 0, 1].iter().cloned() {
for delta_col in [self.width - 1, 0, 1].iter().cloned() {
if delta_row == 0 && delta_col == 0 {
continue;
}
let neighbor_row = (row + delta_row) % self.height;
let neighbor_col = (column + delta_col) % self.width;
let idx = self.get_index(neighbor_row, neighbor_col);
count += self.cells[idx];
}
}
count
}
pub fn toggle_cell(&mut self, row: u32, column: u32) {
let idx = self.get_index(row, column);
self.cells[idx] = if self.cells[idx] == 0 { 1 } else { 0 };
}
}
// パフォーマンス計測のためのタイマー
#[wasm_bindgen]
pub struct Timer {
name: String,
}
#[wasm_bindgen]
impl Timer {
#[wasm_bindgen(constructor)]
pub fn new(name: &str) -> Timer {
console::time_with_label(name);
Timer { name: name.to_string() }
}
}
impl Drop for Timer {
fn drop(&mut self) {
console::time_end_with_label(&self.name);
}
}
# Rustツールチェーンの設定
# wasm-packのインストール
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
# WebAssemblyターゲットの追加
rustup target add wasm32-unknown-unknown
# wasm-packでビルド(ブラウザ向け)
wasm-pack build --target web
# wasm-packでビルド(Node.js向け)
wasm-pack build --target nodejs
# wasm-packでビルド(バンドラー向け: webpack等)
wasm-pack build --target bundler
# 手動ビルド
cargo build --target wasm32-unknown-unknown --release
wasm-bindgen target/wasm32-unknown-unknown/release/wasm_example.wasm \
--out-dir ./pkg --target web
6.3 Go
Go言語は標準のコンパイラでWebAssemblyターゲットをサポートしている。
// main.go
package main
import (
"fmt"
"syscall/js"
)
// フィボナッチ数列の計算
func fibonacci(this js.Value, args []js.Value) interface{} {
n := args[0].Int()
if n <= 1 {
return n
}
a, b := 0, 1
for i := 2; i <= n; i++ {
a, b = b, a+b
}
return b
}
// 配列のソート(クイックソート)
func quickSort(arr []int, low, high int) {
if low < high {
pivot := arr[high]
i := low - 1
for j := low; j < high; j++ {
if arr[j] < pivot {
i++
arr[i], arr[j] = arr[j], arr[i]
}
}
arr[i+1], arr[high] = arr[high], arr[i+1]
pi := i + 1
quickSort(arr, low, pi-1)
quickSort(arr, pi+1, high)
}
}
func sortArray(this js.Value, args []js.Value) interface{} {
jsArray := args[0]
length := jsArray.Length()
// JavaScriptの配列をGoのスライスに変換
arr := make([]int, length)
for i := 0; i < length; i++ {
arr[i] = jsArray.Index(i).Int()
}
// ソート実行
quickSort(arr, 0, length-1)
// 結果をJavaScriptの配列として返す
result := js.Global().Get("Array").New(length)
for i, v := range arr {
result.SetIndex(i, v)
}
return result
}
// DOMの操作
func updateDOM(this js.Value, args []js.Value) interface{} {
document := js.Global().Get("document")
element := document.Call("getElementById", args[0].String())
element.Set("innerHTML", args[1].String())
return nil
}
func main() {
fmt.Println("Go WebAssembly Module Loaded")
// JavaScriptのグローバルスコープに関数を登録
js.Global().Set("goFibonacci", js.FuncOf(fibonacci))
js.Global().Set("goSortArray", js.FuncOf(sortArray))
js.Global().Set("goUpdateDOM", js.FuncOf(updateDOM))
// プログラムを終了させないようにチャンネルでブロック
select {}
}
# GoでWebAssemblyをビルド
GOOS=js GOARCH=wasm go build -o main.wasm main.go
# 必要なJavaScriptサポートファイルのコピー
cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" .
# TinyGoを使用する場合(小さなバイナリサイズ)
# TinyGoのインストール
brew install tinygo # macOS
# TinyGoでビルド
tinygo build -o main.wasm -target wasm main.go
<!-- Go WebAssemblyの使用 -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Go WebAssembly Example</title>
</head>
<body>
<h1>Go WebAssembly</h1>
<div id="output"></div>
<script src="wasm_exec.js"></script>
<script>
const go = new Go();
WebAssembly.instantiateStreaming(
fetch("main.wasm"),
go.importObject
).then((result) => {
go.run(result.instance);
// Go関数の呼び出し
console.log("fib(10) =", goFibonacci(10)); // 55
console.log("sorted:", goSortArray([5, 3, 1, 4, 2]));
goUpdateDOM("output", "<p>Hello from Go!</p>");
});
</script>
</body>
</html>
6.4 AssemblyScript
AssemblyScriptは、TypeScriptのサブセットからWebAssemblyにコンパイルされる言語である。TypeScript開発者にとって最も学習コストが低い選択肢である。
// assembly/index.ts (AssemblyScript)
// フィボナッチ数列
export function fibonacci(n: i32): i32 {
if (n <= 1) return n;
let a: i32 = 0;
let b: i32 = 1;
for (let i: i32 = 2; i <= n; i++) {
const temp = b;
b = a + b;
a = temp;
}
return b;
}
// 配列操作
export function sumArray(arr: Int32Array): i32 {
let sum: i32 = 0;
for (let i = 0; i < arr.length; i++) {
sum += unchecked(arr[i]);
}
return sum;
}
// 文字列操作
export function reverseString(str: string): string {
let result = "";
for (let i = str.length - 1; i >= 0; i--) {
result += str.charAt(i);
}
return result;
}
// 画像処理:セピア調変換
export function sepia(
pixels: Uint8ClampedArray,
width: i32,
height: i32
): void {
for (let i = 0; i < width * height * 4; i += 4) {
const r = unchecked(pixels[i]);
const g = unchecked(pixels[i + 1]);
const b = unchecked(pixels[i + 2]);
unchecked(pixels[i] = min(255, (i32)(r * 0.393 + g * 0.769 + b * 0.189)) as u8);
unchecked(pixels[i + 1] = min(255, (i32)(r * 0.349 + g * 0.686 + b * 0.168)) as u8);
unchecked(pixels[i + 2] = min(255, (i32)(r * 0.272 + g * 0.534 + b * 0.131)) as u8);
}
}
function min(a: i32, b: i32): i32 {
return a < b ? a : b;
}
# AssemblyScriptのセットアップ
npm init
npm install --save-dev assemblyscript
npx asinit .
# ビルド
npm run asbuild
# またはCLI直接
npx asc assembly/index.ts \
--target release \
--outFile build/release.wasm \
--textFile build/release.wat \
--sourceMap \
--optimize
7. WebAssemblyの高度な機能
7.1 SIMD(Single Instruction, Multiple Data)
WebAssembly SIMDは、128ビットのベクトル演算をサポートし、データ並列処理を可能にする。画像処理、音声処理、物理シミュレーションなどの計算集約的なタスクで大幅なパフォーマンス向上を実現する。
;; SIMD演算の例:4つのf32を同時に加算
(module
(memory (export "memory") 1)
;; 4つのf32ベクトルの加算
(func $vec4_add (param $offset_a i32) (param $offset_b i32) (param $offset_result i32)
local.get $offset_result
;; メモリからv128値をロード
(v128.load (local.get $offset_a))
(v128.load (local.get $offset_b))
;; 4つのf32を同時に加算
f32x4.add
;; 結果をメモリに格納
v128.store
)
;; ドット積の計算
(func $dot_product (param $offset_a i32) (param $offset_b i32) (result f32)
;; a * b(要素ごとの乗算)
(v128.load (local.get $offset_a))
(v128.load (local.get $offset_b))
f32x4.mul
;; 水平加算で全要素の合計を取得
;; [x, y, z, w] → x + y + z + w
(local $product v128)
;; 簡略化のため、extractして加算
(f32.add
(f32.add
(f32x4.extract_lane 0 (local.get $product))
(f32x4.extract_lane 1 (local.get $product))
)
(f32.add
(f32x4.extract_lane 2 (local.get $product))
(f32x4.extract_lane 3 (local.get $product))
)
)
)
(export "vec4_add" (func $vec4_add))
)
// C/C++でのSIMD使用例(Emscripten)
#include <wasm_simd128.h>
#include <emscripten.h>
// SIMD を使用した画像のブライトネス調整
EMSCRIPTEN_KEEPALIVE
void adjust_brightness_simd(uint8_t* pixels, int length, int brightness) {
v128_t bright_vec = wasm_i16x8_splat((int16_t)brightness);
v128_t zero = wasm_i16x8_splat(0);
v128_t max_val = wasm_i16x8_splat(255);
for (int i = 0; i < length; i += 16) {
// 8ビットピクセルを16ビットに拡張
v128_t pixels_vec = wasm_v128_load(&pixels[i]);
v128_t low = wasm_u16x8_extend_low_u8x16(pixels_vec);
v128_t high = wasm_u16x8_extend_high_u8x16(pixels_vec);
// ブライトネスを加算
low = wasm_i16x8_add(low, bright_vec);
high = wasm_i16x8_add(high, bright_vec);
// クランプ(0-255の範囲に制限)
low = wasm_i16x8_max(low, zero);
low = wasm_i16x8_min(low, max_val);
high = wasm_i16x8_max(high, zero);
high = wasm_i16x8_min(high, max_val);
// 16ビットから8ビットに戻す
v128_t result = wasm_u8x16_narrow_i16x8(low, high);
wasm_v128_store(&pixels[i], result);
}
}
7.2 マルチスレッド(Threads)
WebAssemblyのスレッドサポートにより、SharedArrayBufferとアトミック操作を使用した並列処理が可能になる。
// C/C++でのマルチスレッド使用例(Emscripten + pthreads)
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <emscripten.h>
#define NUM_THREADS 4
#define ARRAY_SIZE 1000000
typedef struct {
double* array;
int start;
int end;
double result;
} ThreadData;
void* partial_sum(void* arg) {
ThreadData* data = (ThreadData*)arg;
double sum = 0.0;
for (int i = data->start; i < data->end; i++) {
sum += data->array[i];
}
data->result = sum;
return NULL;
}
EMSCRIPTEN_KEEPALIVE
double parallel_sum(double* array, int size) {
pthread_t threads[NUM_THREADS];
ThreadData thread_data[NUM_THREADS];
int chunk_size = size / NUM_THREADS;
// スレッドの作成
for (int i = 0; i < NUM_THREADS; i++) {
thread_data[i].array = array;
thread_data[i].start = i * chunk_size;
thread_data[i].end = (i == NUM_THREADS - 1) ? size : (i + 1) * chunk_size;
thread_data[i].result = 0.0;
pthread_create(&threads[i], NULL, partial_sum, &thread_data[i]);
}
// スレッドの終了を待機
double total = 0.0;
for (int i = 0; i < NUM_THREADS; i++) {
pthread_join(threads[i], NULL);
total += thread_data[i].result;
}
return total;
}
# マルチスレッド対応のビルド
emcc parallel.c -o parallel.js \
-s USE_PTHREADS=1 \
-s PTHREAD_POOL_SIZE=4 \
-s SHARED_MEMORY=1 \
-s EXPORTED_FUNCTIONS='["_parallel_sum", "_malloc", "_free"]' \
-O3
7.3 例外処理(Exception Handling)
WebAssemblyの例外処理プロポーザルにより、C++の例外やその他の言語の例外機構をネイティブにサポートする。
;; 例外処理の例
(module
;; 例外タグの定義
(tag $my_error (param i32)) ;; i32のエラーコードを持つ例外
(func $may_throw (param $input i32) (result i32)
;; 入力が負の場合、例外をスロー
(if (i32.lt_s (local.get $input) (i32.const 0))
(then
(throw $my_error (i32.const -1))
)
)
;; 正常処理
(i32.mul (local.get $input) (i32.const 2))
)
(func $safe_call (param $input i32) (result i32)
(try (result i32)
(do
(call $may_throw (local.get $input))
)
(catch $my_error
;; 例外をキャッチし、エラーコードを返す
;; スタック上にはi32のエラーコードがある
;; エラー時は-1を返す
(drop) ;; エラーコードを破棄
(i32.const -1)
)
(catch_all
;; すべての例外をキャッチ
(i32.const -2)
)
)
)
(export "safe_call" (func $safe_call))
)
7.4 ガベージコレクション(GC)統合
WebAssembly GCプロポーザルにより、ホスト環境のガベージコレクタと統合されたメモリ管理が可能になる。これにより、Java、Kotlin、DartなどのGC言語からWebAssemblyへのコンパイルが効率的になる。
;; GC型の例(提案段階)
(module
;; 構造体型の定義
(type $point (struct
(field $x f64)
(field $y f64)
))
;; 配列型の定義
(type $points (array (ref $point)))
;; 構造体の作成
(func $new_point (param $x f64) (param $y f64) (result (ref $point))
(struct.new $point
(local.get $x)
(local.get $y)
)
)
;; 構造体のフィールドアクセス
(func $distance (param $p1 (ref $point)) (param $p2 (ref $point)) (result f64)
(f64.sqrt
(f64.add
(f64.mul
(f64.sub
(struct.get $point $x (local.get $p1))
(struct.get $point $x (local.get $p2))
)
(f64.sub
(struct.get $point $x (local.get $p1))
(struct.get $point $x (local.get $p2))
)
)
(f64.mul
(f64.sub
(struct.get $point $y (local.get $p1))
(struct.get $point $y (local.get $p2))
)
(f64.sub
(struct.get $point $y (local.get $p1))
(struct.get $point $y (local.get $p2))
)
)
)
)
)
(export "new_point" (func $new_point))
(export "distance" (func $distance))
)
7.5 テールコール最適化
テールコール最適化プロポーザルにより、再帰的なアルゴリズムをスタックオーバーフローなしに実行できるようになる。
;; テールコール最適化の例
(module
;; テールコール版フィボナッチ
(func $fib_tail (param $n i32) (param $a i32) (param $b i32) (result i32)
(if (result i32) (i32.eqz (local.get $n))
(then (local.get $a))
(else
;; return_call はテールコール最適化を保証
(return_call $fib_tail
(i32.sub (local.get $n) (i32.const 1))
(local.get $b)
(i32.add (local.get $a) (local.get $b))
)
)
)
)
(func $fibonacci (param $n i32) (result i32)
(call $fib_tail (local.get $n) (i32.const 0) (i32.const 1))
)
(export "fibonacci" (func $fibonacci))
)
8. WebAssemblyのセキュリティモデル
8.1 サンドボックス実行
WebAssemblyは、強力なセキュリティモデルを持つサンドボックス内で実行される。以下にWebAssemblyのセキュリティ特性を示す。
WebAssemblyのセキュリティ境界
+---------------------------------------------------+
| Host Environment |
| |
| +---------------------------------------------+ |
| | WebAssembly Sandbox | |
| | | |
| | +---------------------------------------+ | |
| | | Linear Memory | | |
| | | ・バウンドチェック済みアクセス | | |
| | | ・ホストメモリから分離 | | |
| | | ・オーバーフロー不可 | | |
| | +---------------------------------------+ | |
| | | |
| | +---------------------------------------+ | |
| | | Execution Stack | | |
| | | ・型安全な操作のみ | | |
| | | ・直接アクセス不可 | | |
| | | ・制御フローの整合性保証 | | |
| | +---------------------------------------+ | |
| | | |
| | +---------------------------------------+ | |
| | | Function Table | | |
| | | ・型チェック済み間接呼び出し | | |
| | | ・不正な関数ポインタの防止 | | |
| | +---------------------------------------+ | |
| | | |
| | 許可された操作のみ: | |
| | ✓ インポートされた関数の呼び出し | |
| | ✓ 線形メモリの読み書き | |
| | ✓ テーブル経由の間接呼び出し | |
| | ✗ 任意のシステムコール | |
| | ✗ ホストメモリの直接アクセス | |
| | ✗ ネットワークアクセス(直接) | |
| | ✗ ファイルシステムアクセス(直接) | |
| +---------------------------------------------+ |
| |
+---------------------------------------------------+
8.2 メモリ安全性
WebAssemblyは以下のメモリ安全性の保証を提供する。
- バウンドチェック: すべてのメモリアクセスは線形メモリの範囲内であることが検証される
- 型安全性: すべての操作は型チェックされ、型混同攻撃を防止する
- 制御フロー整合性(CFI): 間接呼び出しは型チェック済みテーブルを通じてのみ行われる
- スタック保護: 実行スタックはプログラムからアクセスできない
// セキュリティを考慮したWebAssemblyの使用例
class SecureWasmLoader {
constructor() {
this.instances = new Map();
}
async loadModule(url, importPolicy = {}) {
// Content Security Policy (CSP) の確認
// wasm-unsafe-eval が必要
const response = await fetch(url);
// Content-Typeの検証
const contentType = response.headers.get('Content-Type');
if (contentType !== 'application/wasm') {
console.warn('Unexpected Content-Type for Wasm module:', contentType);
}
// インポートの制限
const safeImports = this.sanitizeImports(importPolicy);
const { instance, module } = await WebAssembly.instantiateStreaming(
response,
safeImports
);
// メモリサイズの制限
if (instance.exports.memory) {
const memoryPages = instance.exports.memory.buffer.byteLength / 65536;
if (memoryPages > importPolicy.maxMemoryPages || 100) {
throw new Error('Memory limit exceeded');
}
}
return instance;
}
sanitizeImports(policy) {
const imports = {};
// 必要最小限の関数のみを提供
if (policy.allowConsole) {
imports.env = {
log: (msg) => console.log('[Wasm]:', msg),
};
}
// メモリの制限付き提供
if (policy.memory) {
imports.env = imports.env || {};
imports.env.memory = new WebAssembly.Memory({
initial: policy.memory.initial || 1,
maximum: policy.memory.maximum || 10,
});
}
return imports;
}
}
8.3 Content Security Policy(CSP)
WebAssemblyをブラウザで使用する場合、CSPヘッダーの適切な設定が必要である。
# WebAssemblyを許可するCSPヘッダー
# WebAssembly 2.0以降推奨
Content-Security-Policy: script-src 'wasm-unsafe-eval';
# レガシー互換性
Content-Security-Policy: script-src 'unsafe-eval';
# より厳格な設定例
Content-Security-Policy:
default-src 'self';
script-src 'self' 'wasm-unsafe-eval';
connect-src 'self';
img-src 'self' data:;
style-src 'self' 'unsafe-inline';
9. パフォーマンス最適化
9.1 コンパイル戦略
WebAssemblyランタイムは、複数のコンパイル戦略を使用してパフォーマンスを最適化する。
コンパイルパイプライン
Wasm バイナリ
↓
+---------------------------+
| バリデーション | ← 型チェック、構造検証
+---------------------------+
↓
+---------------------------+ +---------------------------+
| Baseline Compiler | OR | Optimizing Compiler |
| (高速コンパイル) | | (最適化コンパイル) |
| - Liftoff (V8) | | - TurboFan (V8) |
| - 低最適化 | | - 高最適化 |
| - 即時実行可能 | | - コンパイル時間は長い |
+---------------------------+ +---------------------------+
↓ ↓
+---------------------------+ +---------------------------+
| 初期実行 | | ホットコードの |
| (ベースラインコード) | | 再コンパイル |
+---------------------------+ +---------------------------+
↓
Tiered Compilation(段階的コンパイル)
1. まずBaselineで素早くコンパイル
2. 実行中にプロファイリング
3. ホットな関数をOptimizingで再コンパイル
4. コードを差し替え(on-stack replacement)
9.2 最適化テクニック
// パフォーマンス最適化のテクニック
// 1. メモリアクセスの最適化
// 悪い例:ランダムアクセス
void bad_access(float* matrix, int rows, int cols) {
for (int j = 0; j < cols; j++) {
for (int i = 0; i < rows; i++) {
matrix[i * cols + j] *= 2.0f; // キャッシュミスが多い
}
}
}
// 良い例:シーケンシャルアクセス
void good_access(float* matrix, int rows, int cols) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
matrix[i * cols + j] *= 2.0f; // キャッシュフレンドリー
}
}
}
// 2. SIMD活用
#include <wasm_simd128.h>
void multiply_arrays_simd(float* a, float* b, float* result, int length) {
int simd_length = length - (length % 4);
for (int i = 0; i < simd_length; i += 4) {
v128_t va = wasm_v128_load(&a[i]);
v128_t vb = wasm_v128_load(&b[i]);
v128_t vr = wasm_f32x4_mul(va, vb);
wasm_v128_store(&result[i], vr);
}
// 残りの要素
for (int i = simd_length; i < length; i++) {
result[i] = a[i] * b[i];
}
}
// 3. ループアンローリング
void sum_unrolled(float* array, int length, float* result) {
float sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0;
int i;
for (i = 0; i + 3 < length; i += 4) {
sum0 += array[i];
sum1 += array[i + 1];
sum2 += array[i + 2];
sum3 += array[i + 3];
}
*result = sum0 + sum1 + sum2 + sum3;
for (; i < length; i++) {
*result += array[i];
}
}
9.3 バイナリサイズの最適化
# Emscripten でのサイズ最適化
emcc app.c -o app.wasm \
-Oz \ # サイズ最適化
-s MINIMAL_RUNTIME=1 \ # 最小ランタイム
-s STANDALONE_WASM=1 \ # スタンドアロン
--no-entry \ # エントリポイントなし
-s EXPORTED_FUNCTIONS='["_compute"]'
# Rust でのサイズ最適化
# Cargo.toml
# [profile.release]
# opt-level = "z" # サイズ最適化
# lto = true # リンク時最適化
# codegen-units = 1 # 単一コード生成ユニット
# panic = "abort" # パニック時に即座にabort
# strip = true # シンボルを除去
# wasm-opt によるポスト最適化
wasm-opt -Oz -o optimized.wasm input.wasm
# wasm-snip による未使用コードの除去
wasm-snip --snip-rust-panicking-code optimized.wasm -o final.wasm
# バイナリサイズの確認
wc -c final.wasm
wasm-objdump -h final.wasm # セクションごとのサイズ
9.4 ベンチマークとプロファイリング
// ブラウザでのパフォーマンス計測
async function benchmark() {
const { instance } = await WebAssembly.instantiateStreaming(
fetch('compute.wasm')
);
const iterations = 1000;
// ウォームアップ
for (let i = 0; i < 100; i++) {
instance.exports.compute(i);
}
// 計測
const start = performance.now();
for (let i = 0; i < iterations; i++) {
instance.exports.compute(i);
}
const end = performance.now();
console.log(`Average: ${(end - start) / iterations}ms per call`);
// JavaScript版との比較
const jsStart = performance.now();
for (let i = 0; i < iterations; i++) {
computeJS(i); // 同等のJavaScript実装
}
const jsEnd = performance.now();
console.log(`JavaScript: ${(jsEnd - jsStart) / iterations}ms per call`);
console.log(`Speedup: ${((jsEnd - jsStart) / (end - start)).toFixed(2)}x`);
}
// Chrome DevToolsでのプロファイリング
// 1. DevTools → Performance タブ
// 2. Record → WebAssembly呼び出しを含む操作を実行
// 3. Wasm関数がフレームチャートに表示される
// 4. Source Maps を使用すると元のソースコードにマッピング可能
10. 実践的なユースケース
10.1 画像・動画処理
WebAssemblyは画像処理ライブラリのWeb移植に広く使用されている。
// FFmpeg.wasmを使用した動画変換
import { FFmpeg } from '@ffmpeg/ffmpeg';
import { fetchFile, toBlobURL } from '@ffmpeg/util';
async function convertVideo() {
const ffmpeg = new FFmpeg();
// FFmpeg Wasmモジュールのロード
const baseURL = 'https://unpkg.com/@ffmpeg/core@0.12.4/dist/esm';
await ffmpeg.load({
coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, 'text/javascript'),
wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, 'application/wasm'),
});
// 進捗表示
ffmpeg.on('progress', ({ progress, time }) => {
console.log(`Progress: ${(progress * 100).toFixed(1)}% (${time}ms)`);
});
// ファイルの書き込み
await ffmpeg.writeFile('input.mp4', await fetchFile(videoFile));
// 動画変換の実行
await ffmpeg.exec([
'-i', 'input.mp4',
'-vf', 'scale=1280:720',
'-c:v', 'libx264',
'-preset', 'fast',
'-c:a', 'aac',
'output.mp4'
]);
// 結果の読み取り
const data = await ffmpeg.readFile('output.mp4');
const blob = new Blob([data.buffer], { type: 'video/mp4' });
const url = URL.createObjectURL(blob);
// 動画プレーヤーに設定
document.getElementById('video').src = url;
}
// OpenCV.jsを使用した画像処理
// OpenCVはEmscriptenでWebAssemblyにコンパイル済み
async function processImage() {
// OpenCV.jsのロード
await loadOpenCV();
// 画像の読み込み
const src = cv.imread('inputCanvas');
// グレースケール変換
const gray = new cv.Mat();
cv.cvtColor(src, gray, cv.COLOR_RGBA2GRAY);
// ガウシアンブラー
const blurred = new cv.Mat();
cv.GaussianBlur(gray, blurred, new cv.Size(5, 5), 0);
// Cannyエッジ検出
const edges = new cv.Mat();
cv.Canny(blurred, edges, 50, 150);
// 輪郭検出
const contours = new cv.MatVector();
const hierarchy = new cv.Mat();
cv.findContours(edges, contours, hierarchy, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE);
// 結果の描画
const result = cv.Mat.zeros(src.rows, src.cols, cv.CV_8UC3);
for (let i = 0; i < contours.size(); i++) {
const color = new cv.Scalar(
Math.random() * 255,
Math.random() * 255,
Math.random() * 255
);
cv.drawContours(result, contours, i, color, 2);
}
// 結果の表示
cv.imshow('outputCanvas', result);
// メモリの解放
src.delete();
gray.delete();
blurred.delete();
edges.delete();
contours.delete();
hierarchy.delete();
result.delete();
}
10.2 ゲームエンジン
Unity、Unreal Engine、GodotなどのゲームエンジンがWebAssemblyターゲットをサポートしている。
// カスタムゲームエンジンのWebAssembly統合例
class WasmGameEngine {
constructor(canvas) {
this.canvas = canvas;
this.ctx = canvas.getContext('2d');
this.running = false;
this.lastTime = 0;
}
async init() {
// ゲームエンジンのWasmモジュールをロード
const memory = new WebAssembly.Memory({
initial: 256, // 16MB
maximum: 1024, // 64MB
});
const importObject = {
env: {
memory,
// レンダリングコールバック
draw_rect: (x, y, w, h, r, g, b, a) => {
this.ctx.fillStyle = `rgba(${r},${g},${b},${a / 255})`;
this.ctx.fillRect(x, y, w, h);
},
draw_circle: (x, y, radius, r, g, b, a) => {
this.ctx.beginPath();
this.ctx.arc(x, y, radius, 0, Math.PI * 2);
this.ctx.fillStyle = `rgba(${r},${g},${b},${a / 255})`;
this.ctx.fill();
},
clear_screen: () => {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
},
// 入力API
get_key_state: (keyCode) => {
return this.keyStates[keyCode] ? 1 : 0;
},
get_mouse_x: () => this.mouseX,
get_mouse_y: () => this.mouseY,
// ログ
log_message: (ptr, len) => {
const bytes = new Uint8Array(memory.buffer, ptr, len);
console.log(new TextDecoder().decode(bytes));
},
// 乱数
random: () => Math.random(),
// 時間
get_time: () => performance.now(),
},
};
const { instance } = await WebAssembly.instantiateStreaming(
fetch('game_engine.wasm'),
importObject
);
this.wasm = instance.exports;
this.wasm.init(this.canvas.width, this.canvas.height);
// 入力のセットアップ
this.setupInput();
}
setupInput() {
this.keyStates = {};
this.mouseX = 0;
this.mouseY = 0;
document.addEventListener('keydown', (e) => {
this.keyStates[e.keyCode] = true;
this.wasm.on_key_down(e.keyCode);
});
document.addEventListener('keyup', (e) => {
this.keyStates[e.keyCode] = false;
this.wasm.on_key_up(e.keyCode);
});
this.canvas.addEventListener('mousemove', (e) => {
const rect = this.canvas.getBoundingClientRect();
this.mouseX = e.clientX - rect.left;
this.mouseY = e.clientY - rect.top;
});
}
start() {
this.running = true;
this.lastTime = performance.now();
this.gameLoop();
}
gameLoop() {
if (!this.running) return;
const currentTime = performance.now();
const deltaTime = (currentTime - this.lastTime) / 1000; // 秒単位
this.lastTime = currentTime;
// Wasm側のupdate関数を呼び出し
this.wasm.update(deltaTime);
this.wasm.render();
requestAnimationFrame(() => this.gameLoop());
}
stop() {
this.running = false;
}
}
// 使用例
const engine = new WasmGameEngine(document.getElementById('game-canvas'));
await engine.init();
engine.start();
10.3 科学計算と機械学習
// ONNX Runtime Web(WebAssembly バックエンド)を使用した機械学習推論
import * as ort from 'onnxruntime-web';
async function runInference() {
// WebAssemblyバックエンドの設定
ort.env.wasm.wasmPaths = '/ort-wasm/';
// セッションの作成
const session = await ort.InferenceSession.create(
'model.onnx',
{
executionProviders: ['wasm'],
graphOptimizationLevel: 'all',
}
);
// 入力データの準備
const inputData = new Float32Array(1 * 3 * 224 * 224); // バッチ1, 3チャンネル, 224x224
// ... 画像データの前処理 ...
const inputTensor = new ort.Tensor('float32', inputData, [1, 3, 224, 224]);
const feeds = { input: inputTensor };
// 推論の実行
const results = await session.run(feeds);
const output = results.output.data;
// 結果の後処理
const topK = getTopK(output, 5);
console.log('Top 5 predictions:', topK);
}
// TensorFlow.js WebAssemblyバックエンド
import * as tf from '@tensorflow/tfjs';
import '@tensorflow/tfjs-backend-wasm';
async function tfWasmExample() {
// WebAssemblyバックエンドの設定
await tf.setBackend('wasm');
await tf.ready();
console.log('Backend:', tf.getBackend()); // 'wasm'
// モデルのロードと推論
const model = await tf.loadLayersModel('model/model.json');
// 行列演算のベンチマーク
const a = tf.randomNormal([1000, 1000]);
const b = tf.randomNormal([1000, 1000]);
const start = performance.now();
const result = tf.matMul(a, b);
await result.data(); // 結果を待つ
const end = performance.now();
console.log(`Matrix multiply (1000x1000): ${end - start}ms`);
// メモリの解放
a.dispose();
b.dispose();
result.dispose();
}
10.4 データベースとデータ処理
// SQL.js(SQLiteのWebAssembly版)
import initSqlJs from 'sql.js';
async function sqliteExample() {
// SQLiteのWasmモジュールを初期化
const SQL = await initSqlJs({
locateFile: file => `https://sql.js.org/dist/${file}`
});
// 新しいデータベースを作成
const db = new SQL.Database();
// テーブルの作成
db.run(`
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE,
age INTEGER,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
`);
// データの挿入
const stmt = db.prepare(`
INSERT INTO users (name, email, age) VALUES (?, ?, ?)
`);
const users = [
['Alice', 'alice@example.com', 30],
['Bob', 'bob@example.com', 25],
['Charlie', 'charlie@example.com', 35],
];
users.forEach(([name, email, age]) => {
stmt.run([name, email, age]);
});
stmt.free();
// クエリの実行
const results = db.exec(`
SELECT name, email, age
FROM users
WHERE age >= 25
ORDER BY age DESC
`);
console.log('Query results:', results[0].values);
// データベースのエクスポート(バイナリ)
const binaryArray = db.export();
const blob = new Blob([binaryArray], { type: 'application/x-sqlite3' });
// データベースのクローズ
db.close();
}
// DuckDB-Wasm(分析向けデータベース)
import * as duckdb from '@duckdb/duckdb-wasm';
async function duckdbExample() {
// DuckDB-Wasmの初期化
const JSDELIVR_BUNDLES = duckdb.getJsDelivrBundles();
const bundle = await duckdb.selectBundle(JSDELIVR_BUNDLES);
const worker = new Worker(bundle.mainWorker);
const logger = new duckdb.ConsoleLogger();
const db = new duckdb.AsyncDuckDB(logger, worker);
await db.instantiate(bundle.mainModule, bundle.pthreadWorker);
const conn = await db.connect();
// CSVファイルの読み込みと分析
await db.registerFileURL('data.csv', '/data/sales.csv', 4);
const result = await conn.query(`
SELECT
product_category,
COUNT(*) as total_orders,
SUM(amount) as total_revenue,
AVG(amount) as avg_order_value
FROM read_csv_auto('data.csv')
GROUP BY product_category
ORDER BY total_revenue DESC
`);
console.log(result.toArray());
await conn.close();
await db.terminate();
}
10.5 暗号処理
// libsodiumのWebAssembly版を使用した暗号処理
import _sodium from 'libsodium-wrappers';
async function cryptographyExample() {
await _sodium.ready;
const sodium = _sodium;
// 鍵ペアの生成
const keyPair = sodium.crypto_box_keypair();
console.log('Public key:', sodium.to_hex(keyPair.publicKey));
console.log('Secret key:', sodium.to_hex(keyPair.privateKey));
// 対称鍵暗号(秘密鍵暗号)
const key = sodium.crypto_secretbox_keygen();
const nonce = sodium.randombytes_buf(sodium.crypto_secretbox_NONCEBYTES);
const message = 'Hello, WebAssembly Crypto!';
// 暗号化
const encrypted = sodium.crypto_secretbox_easy(message, nonce, key);
console.log('Encrypted:', sodium.to_hex(encrypted));
// 復号
const decrypted = sodium.crypto_secretbox_open_easy(encrypted, nonce, key);
console.log('Decrypted:', new TextDecoder().decode(decrypted));
// ハッシュ計算
const hash = sodium.crypto_generichash(
sodium.crypto_generichash_BYTES,
'data to hash'
);
console.log('Hash:', sodium.to_hex(hash));
// パスワードハッシュ(Argon2id)
const password = 'my-secure-password';
const hashedPassword = sodium.crypto_pwhash_str(
password,
sodium.crypto_pwhash_OPSLIMIT_MODERATE,
sodium.crypto_pwhash_MEMLIMIT_MODERATE
);
console.log('Hashed password:', hashedPassword);
// パスワード検証
const isValid = sodium.crypto_pwhash_str_verify(hashedPassword, password);
console.log('Password valid:', isValid); // true
}
11. WebAssemblyのエコシステムとツール
11.1 ビルドツールとの統合
// webpack.config.js - WebAssemblyの統合
const path = require('path');
const WasmPackPlugin = require('@aspect-build/rules_wasm/webpack');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
},
experiments: {
asyncWebAssembly: true, // WebAssembly ESM統合を有効化
},
module: {
rules: [
{
test: /\.wasm$/,
type: 'webassembly/async',
},
],
},
plugins: [
new WasmPackPlugin({
crateDirectory: path.resolve(__dirname, 'wasm-crate'),
}),
],
};
// Viteでの設定
// vite.config.js
import { defineConfig } from 'vite';
import wasm from 'vite-plugin-wasm';
import topLevelAwait from 'vite-plugin-top-level-await';
export default defineConfig({
plugins: [
wasm(),
topLevelAwait(),
],
optimizeDeps: {
exclude: ['my-wasm-package'],
},
});
// ESモジュールとしてのWebAssemblyの使用(将来の標準)
// import文でWasmモジュールを直接インポート
import { fibonacci, sumArray } from './compute.wasm';
console.log(fibonacci(10)); // 55
11.2 デバッグツール
WebAssembly デバッグエコシステム
+--------------------------------------------------+
| 開発者ツール |
+--------------------------------------------------+
| |
| ブラウザDevTools |
| ├── Chrome DevTools |
| │ ├── Sources パネル(Wasmソースマップ対応) |
| │ ├── Performance プロファイラ |
| │ └── Memory プロファイラ |
| ├── Firefox Developer Tools |
| │ ├── Debugger(Wasmステップ実行) |
| │ └── Profiler |
| └── Safari Web Inspector |
| |
| コマンドラインツール |
| ├── wasm-objdump(逆アセンブル) |
| ├── wasm-decompile(疑似コード生成) |
| ├── wasm-validate(バリデーション) |
| └── wasm-opt(最適化・解析) |
| |
| ソースマップ |
| ├── DWARF デバッグ情報 |
| │ └── C/C++/Rust → Wasm のマッピング |
| └── Name Section |
| └── 関数名・ローカル変数名の保持 |
| |
+--------------------------------------------------+
# デバッグ情報付きビルド
# Emscripten(DWARF情報付き)
emcc app.c -o app.wasm \
-g \ # デバッグ情報
-gsource-map \ # ソースマップ生成
--source-map-base=http://localhost:8080/
# Rust(DWARF情報付き)
RUSTFLAGS="-C debuginfo=2" \
cargo build --target wasm32-unknown-unknown
# DWARFデバッグ情報の確認
llvm-dwarfdump app.wasm
# Chrome DevToolsでのデバッグ
# 1. chrome://flags で #enable-webassembly-debugging を有効化
# 2. C/C++ DevTools Support拡張機能をインストール
# 3. Sources パネルでWasmファイルをブレークポイント設定
11.3 テストフレームワーク
// Rust + wasm-bindgen-test によるWebAssemblyテスト
// tests/web.rs
use wasm_bindgen_test::*;
use my_wasm_lib::*;
// ブラウザでのテスト設定
wasm_bindgen_test_configure!(run_in_browser);
#[wasm_bindgen_test]
fn test_fibonacci() {
assert_eq!(fibonacci(0), 0);
assert_eq!(fibonacci(1), 1);
assert_eq!(fibonacci(10), 55);
assert_eq!(fibonacci(20), 6765);
}
#[wasm_bindgen_test]
fn test_greet() {
let result = greet("World");
assert_eq!(result, "Hello, World! From Rust WebAssembly");
}
#[wasm_bindgen_test]
async fn test_async_operation() {
let result = async_compute(42).await;
assert!(result > 0);
}
# テストの実行
# ブラウザテスト
wasm-pack test --chrome --headless
# Node.jsテスト
wasm-pack test --node
# Firefoxテスト
wasm-pack test --firefox --headless
12. WebAssemblyのクラウド・エッジ活用
12.1 サーバーレスとエッジコンピューティング
WebAssemblyは、サーバーレスやエッジコンピューティングの新しい実行基盤として注目されている。
エッジコンピューティングでのWebAssembly
+-------------------+
| Origin Server |
+-------------------+
↑
+-------------+-------------+
| |
+---------+--------+ +---------+--------+
| Edge Node A | | Edge Node B |
| | | |
| +-------------+ | | +-------------+ |
| | Wasm Runtime| | | | Wasm Runtime| |
| | +---------+ | | | | +---------+ | |
| | | Worker 1| | | | | | Worker 1| | |
| | +---------+ | | | | +---------+ | |
| | | Worker 2| | | | | | Worker 2| | |
| | +---------+ | | | | +---------+ | |
| +-------------+ | | +-------------+ |
+------------------+ +------------------+
↑ ↑ ↑ ↑
+----+ +----+ +----+ +----+
|User| |User| |User| |User|
+----+ +----+ +----+ +----+
利点:
- コールドスタート: ~1ms(コンテナの100倍高速)
- メモリ使用量: 数MB(コンテナの1/100)
- セキュリティ: サンドボックス分離
- ポータビリティ: どのエッジノードでも実行可能
// Cloudflare Workers(WebAssembly対応)
// src/worker.js
import wasmModule from './optimized.wasm';
export default {
async fetch(request, env) {
const instance = await WebAssembly.instantiate(wasmModule);
const { process_request } = instance.exports;
const url = new URL(request.url);
const path = url.pathname;
// URLパスに基づいてWasm関数を呼び出し
if (path === '/api/compute') {
const body = await request.json();
const result = process_request(body.input);
return new Response(JSON.stringify({ result }), {
headers: { 'Content-Type': 'application/json' },
});
}
return new Response('Not Found', { status: 404 });
},
};
# Cloudflare Workers設定(wrangler.toml)
name = "my-wasm-worker"
main = "src/worker.js"
compatibility_date = "2024-01-01"
[build]
command = "cargo build --target wasm32-unknown-unknown --release && wasm-opt -Oz target/wasm32-unknown-unknown/release/my_worker.wasm -o src/optimized.wasm"
12.2 コンテナとWebAssembly
# Docker + WasmEdge の例
# Dockerfile.wasm
FROM scratch
COPY ./app.wasm /app.wasm
ENTRYPOINT ["/app.wasm"]
# Docker Desktop でのWebAssemblyサポート(Beta)
# Docker Desktop の Settings → Features in development → Enable Wasm
# Wasmコンテナのビルドと実行
docker buildx build --platform wasi/wasm -t my-wasm-app .
docker run --runtime=io.containerd.wasmedge.v1 \
--platform=wasi/wasm \
my-wasm-app
# Kubernetes でのWebAssembly(SpinKube / KWasm)
# SpinKubeの例
kubectl apply -f - <<EOF
apiVersion: core.spinoperator.dev/v1alpha1
kind: SpinApp
metadata:
name: my-wasm-app
spec:
image: ghcr.io/my-org/my-wasm-app:latest
executor: containerd-shim-spin
replicas: 3
EOF
12.3 プラグインシステム
WebAssemblyはアプリケーションのプラグインシステムとして理想的である。安全なサンドボックス内でサードパーティコードを実行できる。
// Wasmプラグインホストの実装(Wasmtime使用)
use wasmtime::*;
use std::collections::HashMap;
struct PluginHost {
engine: Engine,
plugins: HashMap<String, Instance>,
store: Store<HostState>,
}
struct HostState {
log_messages: Vec<String>,
}
impl PluginHost {
fn new() -> Self {
let engine = Engine::default();
let store = Store::new(&engine, HostState {
log_messages: Vec::new(),
});
PluginHost {
engine,
plugins: HashMap::new(),
store,
}
}
fn load_plugin(&mut self, name: &str, wasm_bytes: &[u8]) -> Result<()> {
let module = Module::new(&self.engine, wasm_bytes)?;
// ホスト関数の定義(プラグインが呼び出せるAPI)
let log_func = Func::wrap(&mut self.store, |caller: Caller<'_, HostState>, ptr: i32, len: i32| {
let memory = caller.get_export("memory")
.and_then(|e| e.into_memory())
.unwrap();
let data = &memory.data(&caller)[ptr as usize..(ptr + len) as usize];
let message = String::from_utf8_lossy(data).to_string();
println!("[Plugin Log]: {}", message);
});
let imports = [log_func.into()];
let instance = Instance::new(&mut self.store, &module, &imports)?;
self.plugins.insert(name.to_string(), instance);
Ok(())
}
fn call_plugin(&mut self, name: &str, func_name: &str, args: &[Val]) -> Result<Vec<Val>> {
let instance = self.plugins.get(name)
.ok_or_else(|| anyhow::anyhow!("Plugin not found: {}", name))?;
let func = instance.get_func(&mut self.store, func_name)
.ok_or_else(|| anyhow::anyhow!("Function not found: {}", func_name))?;
let mut results = vec![Val::I32(0); func.ty(&self.store).results().len()];
func.call(&mut self.store, args, &mut results)?;
Ok(results)
}
}
13. WebAssemblyの現在と将来
13.1 標準化の状況
WebAssemblyの標準化は、W3C WebAssembly Working GroupおよびCommunity Groupによって管理されている。2024年現在の主要プロポーザルの状態を以下にまとめる。
| プロポーザル | フェーズ | 説明 |
|---|---|---|
| SIMD | 5(完了) | 128ビットSIMD演算 |
| Reference Types | 5(完了) | externref, funcref型 |
| Multi-value | 5(完了) | 複数の戻り値 |
| Bulk Memory | 5(完了) | メモリのバルク操作 |
| Tail Call | 5(完了) | テールコール最適化 |
| Exception Handling | 4(標準化中) | try-catch-throw |
| GC | 4(標準化中) | ガベージコレクション統合 |
| Threads | 4(標準化中) | アトミック操作、共有メモリ |
| Component Model | 3(実装中) | モジュール間相互運用 |
| Stack Switching | 2(提案中) | コルーチン、非同期 |
| Memory64 | 3(実装中) | 64ビットメモリアドレス |
| Branch Hinting | 3(実装中) | 分岐予測ヒント |
| Relaxed SIMD | 4(標準化中) | プラットフォーム固有SIMD |
| Multi Memory | 4(標準化中) | 複数の線形メモリ |
13.2 WebAssemblyの将来展望
WebAssemblyの将来には、以下のような発展が期待されている。
-
ユニバーサルランタイム: ブラウザ、サーバー、エッジ、IoTデバイスなど、あらゆる環境で統一的にコードを実行できるプラットフォームとしての確立
-
言語サポートの拡大: Java、Kotlin、Swift、C#などのGC言語がWebAssembly GCを活用してネイティブに近い効率でコンパイルできるようになる
-
コンポーネントモデルの成熟: 異なる言語で書かれたコンポーネントが安全かつ効率的に連携できるようになり、マイクロサービスやプラグインシステムの新しいアーキテクチャが実現する
-
AIとWebAssembly: 機械学習モデルの推論をWebAssemblyで効率的に実行するための最適化が進み、エッジでのAI処理がより一般的になる
-
セキュリティの強化: Control Flow Integrity(CFI)、メモリタグ拡張(MTE)、Capability-based SecurityなどのWebAssemblyネイティブのセキュリティ機能が追加される
WebAssemblyの進化ロードマップ
2017 ─── MVP リリース
│ ・4つの基本型(i32, i64, f32, f64)
│ ・線形メモリ
│ ・関数テーブル
│
2020 ─── 拡張機能の追加
│ ・SIMD
│ ・Reference Types
│ ・Bulk Memory Operations
│
2022 ─── WebAssembly 2.0
│ ・Exception Handling
│ ・Tail Call
│ ・Multi-value
│
2024 ─── コンポーネント時代
│ ・Component Model
│ ・WASI Preview 2
│ ・GC統合
│
2025+ ── ユニバーサルランタイム
・Stack Switching
・Memory64
・Shared-everything Threads
・完全な言語間相互運用
14. まとめ
WebAssembly(Wasm)は、Webプラットフォームの能力を根本的に拡張する技術である。その主要な特徴と価値を以下にまとめる。
14.1 主要な強み
- パフォーマンス: ネイティブコードに近い実行速度を実現し、計算集約的なタスクをWeb上で効率的に実行できる
- 安全性: サンドボックス環境での実行により、メモリ安全性と制御フロー整合性が保証される
- ポータビリティ: 一度コンパイルすれば、ブラウザ、サーバー、エッジなど、あらゆる環境で実行できる
- 言語多様性: C、C++、Rust、Go、AssemblyScriptなど、多数の言語からコンパイル可能である
- 標準化: W3Cによる公式標準として管理され、すべての主要ブラウザでサポートされている
14.2 適用領域
| 領域 | 代表的なユースケース |
|---|---|
| Web | 画像・動画処理、ゲーム、CAD、3Dレンダリング |
| サーバー | サーバーレス関数、マイクロサービス |
| エッジ | CDNワーカー、IoTデバイス |
| デスクトップ | プラグインシステム、組み込みランタイム |
| ブロックチェーン | スマートコントラクト実行環境 |
14.3 選択指針
WebAssemblyの採用を検討する際の指針として以下を参考にされたい。
- WebAssemblyが適している場面: CPU集約的な計算、既存のC/C++/Rustライブラリの移植、セキュアなサードパーティコード実行、クロスプラットフォーム対応
- JavaScriptで十分な場面: DOM操作中心のUI、一般的なWebアプリケーション、小規模なデータ処理、迅速なプロトタイピング
WebAssemblyはJavaScriptの置き換えではなく、補完的な技術として位置づけられる。両者を適材適所で使い分けることで、最大のパフォーマンスと開発効率を実現できる。
参考資料