Upgrade to Pro — share decks privately, control downloads, hide ads and more …

WASMの速さは、境界で決まる

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest. →

 WASMの速さは、境界で決まる

2026年10月22日のサブスレッド勉強会での発表資料です。

本資料では、ローカルで動作するフォトモザイク生成アプリを題材に、WebAssembly(WASM)と純TypeScriptを同一のUI・アルゴリズム・入力条件で比較し、WASMの速度がどこで決まるかを実測しています。
初期のサイズ優先ビルドではWASMがJSより遅かったこと、最適化設定の変更で逆転したこと、計算のみでは約1.4倍であってもUI更新やWorker境界のコストによって差が変わることを示します。
処理をどこまでWASMに任せ、どこからJSに返すかという境界設計、メモリ使用量の増加(約+23%)、フォールバック、Androidでも同じ.wasmを動かす構成について、実装と計測に基づいて整理します。
WASMの採用を検討されているWeb技術者の方に向けた、測定と設計の話です。

本資料で紹介しているフォトモザイクアプリ:
- https://keiji.github.io/photomosaic-wasm/

Avatar for ARIYAMA Keiji

ARIYAMA Keiji

October 21, 2026

More Decks by ARIYAMA Keiji

Other Decks in Technology

Transcript

  1. WebAssembly とはなにか ブラウザ上のマルチプラットフォームな実行環 境。機械語にコンパイルされたプログラムを実 行する仕組み。 W3C(World Wide Web Consortium)の WebAssembly

    Community Group による規 格。2017 年に初版公開。 スタックベースの仮想マシン向けのバイナリコ ードフォーマット。悪意あるコードからホスト 環境を守るサンドボックス機構を前提とする。 ©2026 ARIYAMA Keiji, C-LIS CO., LTD. All rights reserved. WebAssembly logo by Carlos Baraza / CC0 1.0
  2. ローカルで動く仕組み(オフライン対応) Web はブラウザの Cache Storage に静的アセットを保存。Service Worker は Cache API

    経由で保存を 制御。初回アクセス後はオフラインでも動作する アプリに通信コードを置かない構成(テレメトリ・クラッシュレポート送信、広告リクエストなし) Android は INTERNET パーミッションを設定しない構成 出力画像は EXIF(撮影位置など)を引き継がない仕様 通信コードを置かないことで、ローカル動作を偶然ではなく構造で保証する設計。 ©2026 ARIYAMA Keiji, C-LIS CO., LTD. All rights reserved.
  3. 計算は重い 項目 メイン画像 1920×1080 RGBA ブロック 24 px タイル 300

    枚の距離評価 合成 タイル合成サムネイル 300 枚 値 約 8.3 MB 80×45 = 3,600 ブロック 3,600 × 300 = 108 万回 207 万画素 × 3 チャンネル 64×64×4 B × 300 ≒ 4.9 MB 距離評価は、1 ブロックにつき全タイルを走査。タイル数の増加に比例して計算量が線形に増加。 ©2026 ARIYAMA Keiji, C-LIS CO., LTD. All rights reserved.
  4. 共通コアという選択 Web UI TypeScript Web Worker 同じ .wasm Android UI

    Kotlin + Compose JavaScriptSandbox Android も JNI(Java Native Interface)ではなく JavaScriptEngine 上で同じ .wasm を実行。 ©2026 ARIYAMA Keiji, C-LIS CO., LTD. All rights reserved.
  5. WASMとJS、どちらが速いか 同じ UI、同じアルゴリズム、同じ入力で、WASM 版と純 TypeScript 版を比較。 WASM 版: Rust コアを

    wasm-pack でビルド JS 版: Rust コアを TypeScript に移植した比較用ページ 出力 RGBA は全条件で一致(不一致 0 バイト) 同じアルゴリズムでの比較。差は実行環境の差。 JS版(作ったもの): https://keiji.github.io/photomosaic-wasm/index_js.html ©2026 ARIYAMA Keiji, C-LIS CO., LTD. All rights reserved.
  6. 計測条件 項目 メイン画像 タイル ブロックサイズ 条件 row_batch 8 ブラウザ 計測区間

    回数 出力 Chrome 155 ヘッドレス 1920×1080 RGBA 300 枚(合成サムネイル 64×64) 24 px 生成クリック → 保存有効化(Worker RPC、P1〜P4、Canvas 描画を含む) ウォームアップ後 5 回の中央値 JS と WASM でチェックサム一致 コア単体の Node 計測と、ブラウザの Worker 計測は別物。今回はブラウザ実測を重視。 ©2026 ARIYAMA Keiji, C-LIS CO., LTD. All rights reserved.
  7. 初期計測: JSのほうが速かった 最初の WASM は opt-level = "z" のサイズ優先ビルド。 実装

    run WASM(opt-level = "z") 約 334 ms 約 270 ms 純 TypeScript 合計(set main + add tiles + run) 約 382 ms 約 332 ms サイズ優先ビルドと JS↔WASM の境界コピーが、計算の差を上回る要因。 ©2026 ARIYAMA Keiji, C-LIS CO., LTD. All rights reserved.
  8. opt-level = 3 で逆転 opt-level = 3 への変更で、計算は JS を上回る結果。

    350 300 250 )sm( nur 200 150 100 50 0 純 TypeScript WASM opt-level=z 実装 run は約 230 ms(純 TypeScript 約 270 ms)。約 31% 改善 合計は約 274 ms(純 TypeScript 約 332 ms)。約 17% 高速 バイナリは 176 KB → 191 KB(+9%)。500 KB 上限内 ©2026 ARIYAMA Keiji, C-LIS CO., LTD. All rights reserved. WASM opt-level=3
  9. 16msのUI更新が差を飲み込む メインスレッド版は、行バッチごとに requestAnimationFrame(rAF) で UI に制御を返す方 式。 row_batch WASM 8

    約 450 ms 約 365 ms 32 純 TypeScript 約 508 ms 約 378 ms 1 バッチあたり約 16 ms の待ち row_batch = 8 では 12 バッチ、待ちは約 176 ms 計算差より待ち時間が大きい UI更新のための待ち時間が、計算の差を飲み込む要因。 ©2026 ARIYAMA Keiji, C-LIS CO., LTD. All rights reserved. 差 約 11% 約 3%
  10. Workerで境界を消す WASM 版と JS 版の両方で、コアを Web Worker 内で実行。 メインスレッドの rAF

    を廃止し、Worker 内で setTimeout(0) に置き換え 進捗・部分結果は postMessage、画像バッファは transferable 生成中も UI は操作可能。キャンセルはバッチ境界で効く 実装 中央値 JS Worker 比 JS Worker 336 ms - WASM Worker(opt-level = 3) 237 ms WASM Worker(opt-level = "z") 345 ms 約 -29% 約 +3% Worker 化で、計算差がそのまま性能差。 ©2026 ARIYAMA Keiji, C-LIS CO., LTD. All rights reserved.
  11. メモリはWASMが重い 速度の代償はメモリ。タイル 1,000 枚の条件での比較。 時点 タイル読み込み後 生成完了後(保持) ページ全体で +9.22 MB(+22.7%)

    Worker だけで +9.26 MB(+30.4%) WASM の linear memory は縮まない ©2026 ARIYAMA Keiji, C-LIS CO., LTD. All rights reserved. JS WASM 40.59 MB 49.86 MB 40.62 MB 49.84 MB
  12. 速度差は条件依存 同じワークロードでも、条件による結論の変化。 条件 計算のみ(opt-level = 3) rAF 待ちあり(row_batch = 8)

    row_batch = 32 メモリ 起動時間 結果 WASM が約 1.4 倍 速い 差は約 11% 差は約 4% WASM が約 23% 多い 未計測 10 倍速くなるという期待は非現実的。計算支配なら 1〜2 倍。境界・待ち・メモリで逆転の可能 性。 ©2026 ARIYAMA Keiji, C-LIS CO., LTD. All rights reserved.
  13. 処理の分担(P0 - P5) P0 読み込み UI P1 解析 WASM P2

    タイル解析 UI + WASM P3 マッチング WASM コアは P1〜P4、P0 と P5 はプラットフォームの標準機能に委任。 ©2026 ARIYAMA Keiji, C-LIS CO., LTD. All rights reserved. P4 合成 WASM P5 保存 UI
  14. 境界を越えるデータ 境界を越えるのは、次の 4 種類だけ。ピクセル単位での受け渡しはなし。 データ メイン画像 RGBA 特徴量サムネイル 合成サムネイル 部分結果(row_batch

    = 8) 完成画像 RGBA サイズ 1920×1080×4 ≒ 8.3 MB 32×32×4 = 4 KB / 枚 64×64×4 = 16 KB / 枚 1920×192×4 ≒ 1.47 MB / バッチ 8.3 MB 4種類だけ。タイル 300 枚でも、特徴量 1.2 MB、合成サムネイル 4.9 MB。 ©2026 ARIYAMA Keiji, C-LIS CO., LTD. All rights reserved. 方向 JS → WASM JS → WASM JS → WASM WASM → JS WASM → JS
  15. 境界はコストになる WASM 呼び出しは、境界を超える回数とメモリのコピーがコストになる。 引数は wasm-bindgen が 1 回コピーする 結果 RGBA

    も WASM メモリから JS 側へコピーされる transferable は Worker の postMessage では効くが、WASM 境界のコピーは消えない ピクセルごとに呼ぶと、計算より境界コストが勝つ Vec<u8> 大きなチャンクをまとめて渡す設計。 ©2026 ARIYAMA Keiji, C-LIS CO., LTD. All rights reserved.
  16. WASMに置くもの、JSに残すもの WASM に置く JS に残す グリッド計算 ブロック特徴量 タイル特徴量 マッチング 合成・色補正

    進捗・キャンセル 画像デコード・縮小 EXIF 回転・白背景合成 Canvas 描画 PNG / JPEG エンコード ダウンロード / MediaStore UI 状態管理 計算はWASM、I/Oと描画はJS。 ©2026 ARIYAMA Keiji, C-LIS CO., LTD. All rights reserved.
  17. 実装: バッチ対応のAPI web/src/core/bridge.ts 1 const drive = (): void =>

    { 2 const out = wasm.step_run(performance.now()); 3 if (out.progress) emitProgress(out.progress); 4 if (out.result) emitResult(out.result); 5 const done = out.done; 6 out.free(); 7 if (done) { resolve(); return; } 8 9 schedule(drive); // rAF or setTimeout(0) }; 1 回の step_run が 1 行バッチ。境界でホストへ制御を返す設計。 ©2026 ARIYAMA Keiji, C-LIS CO., LTD. All rights reserved.
  18. row_batch は「UIに返す粒度」 row_batch は計算の粒度ではなく、UI に制御を返す粒度。 row_batch バッチ数(P3 + P4) 8

    12 32 4 待ち時間(16 ms / バッチ) 約 176 ms 約 48 ms 大きくすると待ちは減るが、1 バッチが長くなり UI が固まりやすい 既定は 8。計測しながら調整する UIに返す粒度としての row_batch 設計。 ©2026 ARIYAMA Keiji, C-LIS CO., LTD. All rights reserved.
  19. 高速化の工夫 高速化は 3 層。WASM の内側、境界、UI/UX。 層 WASM内部 境界 UI/UX 主な工夫

    データ構造とアルゴリズム、前計算、LUT(Lookup Table)、ビルド設定(最適化) メモリコピー削減、transferable、バッチ 非同期処理、部分プレビュー、入力待ち、フォールバック 3 層の分離で、改善対象が明確。 ©2026 ARIYAMA Keiji, C-LIS CO., LTD. All rights reserved.
  20. WASM内部: データ構造とアルゴリズム 工夫 特徴量サムネイル 合成サムネイル 総当たり k-d 木 重複グループ メモリガード

    内容 32×32 に縮小してから渡す(1 枚 4 KB) 長辺 64 px 上限(1 枚 16 KB) タイル 2,000 枚以下は SIMD(Single Instruction, Multiple Data)前提の総当たり 2,000 枚超は 4 次元(L, a, b*, detail)で近傍探索 似たタイルを同一視し、同じ写真の連続使用を防ぐ 合成サムネイル合計 512 MB 超過で解像度を半減 k-d 木(k-dimensional tree)は、色空間を軸ごとに分割して近いタイルを絞る木構造。2,200 枚 でも候補は 32 件まで。 ©2026 ARIYAMA Keiji, C-LIS CO., LTD. All rights reserved.
  21. WASM内部: 1ブロック1回の前計算 core/src/compose.rs 1 let step_x = thumb_w / bw;

    2 let step_y = thumb_h / bh; 3 let origin_x = 0.5 * step_x - 0.5; 4 let origin_y = 0.5 * step_y - 0.5; 5 for y in 0..bh { // 6 let src_y = y * step_y + origin_y; 7 let py0 = src_y.floor(); 8 9 let fy = src_y - py0; let wy0 = 1.0 - fy; 10 let wy1 = fy; 11 for x in 0..bw { /* ... */ } 12 ブロック→サムネイルの一次写像 } 1 ピクセルごとの除算を排除し、 1ブロック1回の前計算。 ©2026 ARIYAMA Keiji, C-LIS CO., LTD. All rights reserved.
  22. WASM内部: 色変換をLUT化 色変換の powf / cbrt を、LUT(ルックアップテーブル)による前計算テーブル + 線形補間に置き 換え。

    変換 工夫 sRGB 8bit → 線形 RGB 256 エントリの LUT 線形 → sRGB 8bit 線形 → L* 4,096 分割 + 線形補間 4,096 分割 + 線形補間 効果 全ピクセルで powf を回避 P4: 2,578 ms → 700 ms(約 3.6 倍) P1: 1,507 ms → 100 ms(約 15 倍) 8bit に丸めた結果は厳密式と一致。 4,096分割の前計算が効果の源。 ©2026 ARIYAMA Keiji, C-LIS CO., LTD. All rights reserved.
  23. WASM内部: ビルド設定(最適化) core-wasm/Cargo.toml 1 [profile.release] 2 opt-level = 3 3

    lto = true 4 codegen-units = 1 5 panic = "abort" 6 strip = true # 速度重視 # unwind コードを削る 7 8 9 [package.metadata.wasm-pack.profile.release] wasm-opt = false # binaryen rustc 未導入のため 最適化のみ サイズ優先の opt-level = "z" から、速度優先の opt-level = 3 へ。 ©2026 ARIYAMA Keiji, C-LIS CO., LTD. All rights reserved.
  24. 境界: メモリコピー削減 工夫 バッチ 出力バッファの再利用 部分結果 同期 API のキャッシュ 一括登録

    内容 RGBA・サムネイルをまとめて渡す メイン画像をシードにし、行バッチで上書き 完成を待たず、行バッチ単位で Canvas へ転写 タイル数・メモリ量は Worker 応答時に保持 Android は addTiles で 300 枚を 1 制御に 境界コストは、呼び出し回数とコピー量で決まる。まとめて渡すほど安い。 ©2026 ARIYAMA Keiji, C-LIS CO., LTD. All rights reserved.
  25. 境界: transferable で所有権を移す は、postMessage で ArrayBuffer の所有権を移す JavaScript / Web

    Worker の 仕組み。Rust の仕様ではない。 transferable 項目 仕組み 効果 注意 限界 内容 の transfer list に ArrayBuffer を指定 コピーせず、所有権だけを移転 送信元では detached になり、再利用には再確保が必要 WASM メモリ境界のコピーは消えない postMessage transferable で、コピーではなく所有権を移転。 ©2026 ARIYAMA Keiji, C-LIS CO., LTD. All rights reserved.
  26. UI/UX: 部分プレビューと入力待ち 重い処理を、ユーザーの操作感に変換。 行バッチごとに部分結果を Canvas へ転写。メイン画像がモザイクに置き換わっていく スライダーはドラッグ中に再生成せず、確定時に発火 Android は、操作が止まって 300

    ms 後に再生成(連続操作をまとめる) 進捗イベントは 16 ms 以上、または全体比率 1% 以上の変化で発火 生成完了後にメトリクスをオーバーレイ表示。JSON をコピーできる 部分結果の可視化で、待ち時間の体感が変化。 ©2026 ARIYAMA Keiji, C-LIS CO., LTD. All rights reserved.
  27. UI/UX: フォールバック WASM が使えない環境でも、アプリは動作する必要がある。 失敗 Worker 初期化失敗 WASM ロード失敗 それも失敗

    メモリ不足 タイル不足 フォールバック メインスレッド版 WASM 純 TypeScript コア モック(UI 開発用) ブロックサイズ・画像サイズの案内 未確定ブロックは背景のまま フォールバック――Graceful Degradation――の用意で、WASM 前提を回避。 ©2026 ARIYAMA Keiji, C-LIS CO., LTD. All rights reserved.
  28. 最適化の順序 推測するな、計測せよ。 1. コア内部への計測埋め込み(バッチ境界・フェーズ境界のみ) 2. ボトルネックの特定(P1 / P2 / P3

    / P4) 3. 一度に一つだけの変更(opt-level、LUT、row_batch) 4. 同じ条件での再計測(画像・タイル・row_batch を固定) 5. CI での性能リグレッション検知 "You can't tell where a program is going to spend its time." by Rob Pike ©2026 ARIYAMA Keiji, C-LIS CO., LTD. All rights reserved.
  29. Web技術者への持ち帰り 1. まず測定。WASM にすれば速い、は思い込み 2. 境界設計。大きく渡し、まとめて返す 3. UI スレッドを塞がない。Worker とバッチ境界

    4. メモリのコスト。linear memory は縮まない 5. フォールバックの用意。JS 版は保険であり比較対象 まず測定。未計測の最適化は、サイズ優先ビルドのような罠。 ©2026 ARIYAMA Keiji, C-LIS CO., LTD. All rights reserved.
  30. WASM 採用の効果とコスト 効果 計算は速くなる 共通コアを再利用できる サンドボックスで安全 性能が安定する コスト メモリ使用量 +23%(JS

    40.6 MB → WASM 49.8 MB) ビルドチェーン・バイナリ 191 KB の維持 デバッグの手間 起動・コンパイルのコスト JS フォールバックの実装・保守コスト JS フォールバックの用意が、WASM 前提を避ける保険。 ©2026 ARIYAMA Keiji, C-LIS CO., LTD. All rights reserved.
  31. WASMの使いどころチェックリスト チェック項目 ホットスポット データ構造 境界 メモリのコスト 既存資産 再利用性 WASM 寄り

    JS 寄り 計算・画像・圧縮・暗号 連続した型付き配列 呼び出しが少ない 約 +23% の余裕 DOM・文字列・I/O Rust / C / C++ Web / Android / Node 小さなオブジェクト 高頻度の細かい呼び出し 厳しい 最適化済み JS Web だけ 3〜4 個なら検討、5〜6 個なら採用候補、1〜2 個なら JS 優先。 ©2026 ARIYAMA Keiji, C-LIS CO., LTD. All rights reserved.
  32. おまけ: Androidでも同じ .wasm Android は JavaScriptEngine(JavaScriptSandbox)上で、Web と同じ .wasm を実行。 項目

    Web Android UI TypeScript + DOM Kotlin + Compose 実行環境 通信・コア 画像 I/O Web Worker JavaScriptSandbox postMessage / 同じ .wasm MessagePort / 同じ .wasm Canvas ImageDecoder / Bitmap JNI でネイティブコードを直接動かさない ©2026 ARIYAMA Keiji, C-LIS CO., LTD. All rights reserved.
  33. 用語集 用語 意味 WASM WebAssembly(ウェブアセンブリ)。ブラウザで実行できるバイナリ命令形式。 SIMD Single Instruction, Multiple Data(単一命令・複数データ)。1命令で複数のデータを並列処理する方式。

    COOP/COEP Cross-Origin Opener Policy / Cross-Origin Embedder Policy。SharedArrayBufferを有効にするためのHTTPレスポンスヘッダ。 EXIF Exchangeable Image File Format(交換可能画像ファイル形式)。撮影日時・位置情報などのメタデータ。 JNI Java Native Interface(Javaネイティブインターフェース)。Java/Kotlinからネイティブコードを呼び出す仕組み。 LUT Lookup Table(ルックアップテーブル)。計算結果を事前に表へ保存し、参照して計算を省略する手法。 k-d 木 k-dimensional tree(k次元木)。色空間を軸ごとに分割し、近いタイルの候補を絞るデータ構造。 transferable postMessage で ArrayBuffer の所有権を移す仕組み。Rust の仕様ではない。 ©2026 ARIYAMA Keiji, C-LIS CO., LTD. All rights reserved.
  34. Appendix: 参考資料 photomosaic-wasm https://github.com/keiji/photomosaic-wasm WebAssembly Specification https://webassembly.github.io/spec/ MDN WebAssembly https://developer.mozilla.org/docs/WebAssembly

    web.dev WebAssembly https://web.dev/articles/webassembly Chrome DevTools Performance https://developer.chrome.com/docs/devtools/performance/ ©2026 ARIYAMA Keiji, C-LIS CO., LTD. All rights reserved.