Contents
1. 最適化フロー全体像
| フェーズ | 主な作業 | Acme 推奨ツール |
|---|---|---|
| コード選定 | CPU 負荷が高いロジック(画像フィルタ、暗号化、音声解析など)を Rust に移行 | cargo new、Acme Code‑Review Bot |
| ビルド設定 | LTO・opt‑level の有効化、サイズ最適化フラグの追加 | Cargo, wasm-pack, Acme CI(GitHub Actions) |
| 実装変更 | wasm‑bindgen エクスポート削減、SAB で共有メモリ活用、#[inline] や SIMD の導入 |
wasm-bindgen-cli, wasm-opt |
| ベンチマーク | バイナリサイズ・ロード時間・CPU 時間を測定し、CI に自動記録 | cargo-criterion, Acme Bench‑Dashboard |
| 結果共有 | レポートを Markdown/スプレッドシートに出力し、プルリクエストで添付 | GitHub, Acme Docs |
このサイクルは 「コード → ビルド → 実装 → 測定 → 改善」 の 5 ステップが循環することで、最適化効果を 可視化 しつつ開発速度を保ちます。
ポイント:Acme Platform では CI にベンチマーク結果の自動集計パイプライン(
acme-bench-report.yml)が用意されているため、最適化前後の差分はプルリクエストに自動で添付されます。
2. wasm‑bindgen のエクスポート削減テクニック
2‑1. 必要な関数だけを公開する
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
use wasm_bindgen::prelude::*; /// 公開が必要な API のみを残す #[wasm_bindgen] pub fn blur_image(data: &[u8], width: u32, height: u32) -> Vec<u8> { // 画像ブラー本体(省略) vec![] } /// テスト用の内部関数は `skip` で除外 #[wasm_bindgen(skip)] fn helper_fft(input: &[f32]) -> Vec<f32> { // 実装は残すが JS 側には出さない vec![] } |
- 効果:Qiita の実測(2023‑12)では、
skipにより生成された JavaScript ラッパーの行数が約 12 % 減少し、.wasm本体は 8 % 小さくなったことが報告されています【1】。
2‑2. 名前衝突を防ぐ js_name
|
1 2 3 4 5 6 |
#[wasm_bindgen(js_name = "encrypt")] pub fn encrypt_data(key: &str, plaintext: &[u8]) -> Vec<u8> { // 暗号化ロジック… vec![] } |
- 効果:JavaScript 側の可読性が向上し、
webpackのツリーシェイキングが有効になることでロード時スクリプトサイズが数パーセント削減できます(Acme Bundle Analyzer の測定結果)【2】。
2‑3. pub(crate) だけでは不十分
Rust のデッドコード除去は最適化段階でのみ機能しますが、wasm-bindgen はシンボル情報をビルド前に走査するため、明示的に #[wasm_bindgen(skip)] を付与 しないと不要関数がバンドルされます。
3. Cargo ビルド設定 ― LTO・サイズ最適化
3‑1. 推奨 Cargo.toml プロファイル
|
1 2 3 4 5 6 7 |
[profile.release] opt-level = "z" # サイズ優先(gzip 圧縮率が最大) lto = true # Link Time Optimization codegen-units = 1 # 最適化粒度を最小化 panic = "abort" # パニック時のスタックトレース削除 strip = true # デバッグ情報除去(Rust 1.61+) |
- ベンチマーク根拠:Acme CI の内部テスト(2024‑02)で、同一コードベースを
opt-level = "z"+ LTO でビルドした場合、.wasmサイズは 最大 29 % 短縮し、実行時間は +3 %〜+7 % の範囲に収まることが確認されています【3】。
3‑2. opt-level と codegen-units のトレードオフ
| opt‑level | codegen‑units | ビルド時間(CI) | バイナリサイズ |
|---|---|---|---|
"s" |
4 | 中 | 小 |
"z" |
1 | 長(+15 %) | 最小 |
3 |
1 | 長(+20 %) | やや大 |
実務では、CI のリソースとデプロイサイズのバランスを見て上記設定が標準となります。
4‑1. SAB の基本概念と効果
| 項目 | 従来の Uint8Array コピー |
SAB を使った共有メモリ |
|---|---|---|
| データ転送コスト | メモリコピーが発生(O(N)) | 同一バッファ上でポインタだけ渡すため 実質 0 % に近い |
| GC 圧力 | 高(短命オブジェクト多数) | 低(同一オブジェクトを再利用) |
| ブラウザ対応 | 全ブラウザ | Chrome・Edge・Firefox (≥79) は COOP/COEP が必須【4】 |
注意:SAB が「コピーコスト 0 %」になるわけではなく、ポインタの受渡しだけで済むため実測では 95 %〜99 % の削減が得られる と報告されています(Acme Lab の内部計測)【5】。
4‑2. JavaScript 側実装例
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// src/wasm_loader.js (Acme Frontend) export async function initWasm() { // 1MiB の共有バッファを確保(サイズは用途に合わせて調整) const sab = new SharedArrayBuffer(1024 * 1024); const inView = new Uint8Array(sab, 0, 512 * 1024); const outView = new Uint8Array(sab, 512 * 1024, 512 * 1024); // データを書き込む(例: Canvas から取得したピクセル列) inView.set(fetchImageData()); // WASM のエクスポート関数にポインタと長さだけ渡す const { process } = await getWasmExports(); // async init (see §6) process(inView.byteOffset, inView.length, outView.byteOffset, outView.length); // 結果を取得して Canvas に描画 renderImage(outView); } |
4‑3. Rust 側実装例(no_mangle 関数)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// src/lib.rs #[no_mangle] pub extern "C" fn process( in_ptr: *const u8, in_len: usize, out_ptr: *mut u8, out_len: usize, ) { // SAFETY: 呼び出し側が有効な SharedArrayBuffer を保証 let input = unsafe { std::slice::from_raw_parts(in_ptr, in_len) }; let output = unsafe { std::slice::from_raw_parts_mut(out_ptr, out_len) }; // 例: 画像のガンマ補正(簡易実装) for (i, pixel) in input.iter().enumerate() { output[i] = ((pixel as f32 / 255.0).powf(2.2) * 255.0) as u8; } } |
- ビルドコマンド(Acme CI 推奨)
|
1 2 3 |
wasm-pack build --target web --release wasm-opt -Oz --gc-sections -o pkg/project_opt.wasm pkg/project_bg.wasm |
- 期待効果:同一ベンチマークシナリオで、
processの呼び出し時間は 30 %〜45 % 短縮(Acme Bench‑Dashboard 2024‑03)【6】。
5. SAB を利用する際のセキュリティ要件(COOP/COEP)
SharedArrayBuffer は Cross‑Origin Isolated なコンテキストでのみ有効です。Acme Platform の Web アプリでは、次のヘッダーを必ず返すように構成してください。
| ヘッダー | 推奨値 | 目的 |
|---|---|---|
Cross-Origin-Opener-Policy |
same-origin |
同一オリジン間でウィンドウ・タブの分離を保証 |
Cross-Origin-Embedder-Policy |
require-corp |
埋め込みリソースが CORP(Cross‑Origin Resource Policy)に準拠していることを要求 |
Access-Control-Allow-Origin |
<自ドメイン> |
CORS の明示的許可 |
Acme 推奨設定例(nginx)
|
1 2 3 4 |
add_header Cross-Origin-Opener-Policy "same-origin" always; add_header Cross-Origin-Embedder-Policy "require-corp" always; add_header Access-Control-Allow-Origin "$http_origin" always; |
実装上の注意
-iframe内で SAB を使う場合、埋め込み先ページも同様に COOP/COEP ヘッダーが必要です。
- 2023 年以降、Firefox はdocument.domainの使用を禁止しているため、サブドメイン間の共有は CORS + COOP/COEP に統一してください【7】。
6. Edge 環境(Cloudflare Workers / Fastly Compute@Edge)へのデプロイ
6‑1. WASI ビルドでの共通化
|
1 2 3 4 5 6 7 8 9 |
# Cargo.toml [lib] crate-type = ["cdylib"] [profile.release] opt-level = "z" lto = true strip = true |
ビルドコマンド(Acme CI 用):
|
1 2 3 |
cargo build --release --target wasm32-wasi # 出力: target/wasm32-wasi/release/my_project.wasm |
6‑2. Cloudflare Workers(wrangler)
|
1 2 3 4 5 6 |
npm install -g @cloudflare/wrangler wrangler login wrangler init my-worker --type rust # Cargo.toml の `[target.wasm32-unknown-unknown]` を `wasm32-wasi` に変更 wrangler publish |
ベネフィット:Acme Edge Cache が自動で .wasm を gzip/ Brotli 圧縮し、レイテンシは 平均 15 ms(東京リージョン)に抑えられます【8】。
6‑3. Fastly Compute@Edge
|
1 2 3 4 |
fastly compute init --language rust my_fastly_project # Cargo.toml に `target = "wasm32-wasi"` を設定し、`cargo build --release` fastly compute publish |
Fastly の公式ベンチマーク(2024‑01)では、CPU 集中型画像変換 API が 30 % 高速化したと報告されています【9】。
7. ベンチマーク・プロファイリングツールの選び方と結果報告例
| ツール | 主な測定対象 | 推奨使用シーン |
|---|---|---|
wasm-bench (Acme 提供) |
ロード・インスタンス化時間、全体実行時間 | CI でのプルリクエスト単位ベンチマーク |
cargo-criterion |
CPU サイクル、ヒープ使用量、統計的有意差 | ライブラリ内部ロジックの微調整 |
| Chrome DevTools (Performance) | JavaScript ↔ WASM 呼び出しコスト、GC 時間 | 手動デバッグ・最適化ポイント特定 |
wasm-opt --metrics |
バイトコードサイズ、関数削除率 | ビルド後のサイズ測定 |
7‑1. ベンチマーク実行例
|
1 2 3 4 5 6 |
# 1. CI 内で wasm-bench を走らせる npm run bench-wasm # → results/bench.json が生成される # 2. cargo-criterion のベンチマーク cargo criterion --bench blur |
ベンチマーク結果(Acme 社内プロジェクト例)
| 項目 | 最適化前 (baseline) | 最適化後 (SAB + LTO) |
|---|---|---|
.wasm サイズ |
1 240 KB | 860 KB (-30 %) |
| ネットワーク取得時間(HTTP/2) | 152 ms | 92 ms (-39 %) |
process 関数実行時間 (画像ブラー 1024×768) |
42 ms | 38 ms (-9.5 %) |
| メモリピーク | 12 MiB | 9 MiB (-25 %) |
※全ての数値は Acme Bench‑Dashboard (2024‑04) に記録された平均値です。
7‑2. レポートテンプレート(Markdown)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# ベンチマーク結果: my_project v1.3.0 ## ビルド構成 - `opt-level = "z"`, LTO 有効 - `wasm-opt -Oz --gc-sections` - SharedArrayBuffer (SAB) 使用、COOP/COEP ヘッダー設定済み ## 測定指標 | 指標 | baseline | 改善版 | |------|----------|--------| | .wasm サイズ | 1 240 KB | 860 KB | | ダウンロード時間 | 152 ms | 92 ms | | 処理時間 (blur) | 42 ms | 38 ms | | メモリ使用量 | 12 MiB | 9 MiB | ## 結論 SAB と LTO の組み合わせで **30 %** のサイズ削減と **約10 %** の実行時間短縮が達成できました。CI に自動レポートを追加し、次回リリースで本設定をデフォルト化します。 |
8. Acme 推奨ツールチェーン & CI 設定例
8‑1. ローカル開発環境
| ツール | バージョン (2024) |
|---|---|
| Rust | 1.78.0 |
| wasm-pack | 0.12.1 |
| wasm-bindgen-cli | 0.2.92 |
binaryen (wasm-opt) |
118 |
| node.js | 20.x |
|
1 2 3 4 5 |
# 初期セットアップ(Acme の devcontainer でも同様) rustup target add wasm32-unknown-unknown wasm32-wasi cargo install wasm-pack wasm-bindgen-cli npm i -g binaryen |
8‑2. GitHub Actions (Acme CI)
.github/workflows/acme-opt.yml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
name: Rust + Wasm 最適化パイプライン on: pull_request: paths: - '**/*.rs' - 'Cargo.toml' jobs: build-and-bench: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 # Rust 環境セットアップ - name: Install toolchain run: | rustup update stable rustup target add wasm32-unknown-unknown wasm32-wasi # ビルド (LTO + size opt) - name: Build WASM (release) run: | cargo build --release --target wasm32-unknown-unknown wasm-pack build --target web --release wasm-opt -Oz --gc-sections -o pkg/project_opt.wasm pkg/project_bg.wasm # ベンチマーク実行 - name: Run wasm-bench run: | npm ci npm run bench-wasm > results/bench.json # 結果をプルリクエストにコメント - uses: actions/github-script@v7 with: script: | const fs = require('fs'); const result = fs.readFileSync('results/bench.json','utf8'); github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, body: `## 🚀 Wasm 最適化ベンチマーク結果\n```json\n${result}\n```` }); |
8‑3. デプロイ自動化(Acme Edge)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# .github/workflows/deploy-edge.yml name: Deploy to Acme Edge on: push: branches: [ main ] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Build WASI binary run: cargo build --release --target wasm32-wasi - name: Publish to Cloudflare Workers env: CF_API_TOKEN: ${{ secrets.CF_API_TOKEN }} run: | wrangler publish ./target/wasm32-wasi/release/my_project.wasm |
参考文献・出典
| 番号 | 内容 | 出典 |
|---|---|---|
| [1] | Qiita 記事「wasm‑bindgen のエクスポート削減でサイズが12 %小さくなる」 | https://qiita.com/Kanahiro/items/1894ceebc49cd48391c5 |
| [2] | Acme 社内の Bundle Analyzer(2024‑01)レポート | 非公開社内資料 |
| [3] | Acme CI における LTO+opt-level = "z" のベンチマーク結果 |
https://github.com/acme/ci-benchmarks |
| [4] | MDN – SharedArrayBuffer の利用条件(COOP/COEP) | https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer |
| [5] | Acme Lab が測定した SAB コピー削減率(2023‑11) | 社内レポート「SAB Performance」 |
| [6] | Acme Bench‑Dashboard に掲載された実装例と速度改善データ | https://bench.acme.com/projects/xyz |
| [7] | Firefox 91 リリースノート – document.domain 非推奨 |
https://firefox-source-docs.mozilla.org/security/ |
| [8] | Cloudflare Workers 公開ベンチマーク(2024‑02) | https://blog.cloudflare.com/introducing-wasm-performance-metrics/ |
| [9] | Fastly Compute@Edge パフォーマンス比較(2023‑12) | https://www.fastly.com/blog/compute-at-edge-performance |
まとめ
- 全体フローを循環させることで、最適化効果が測定可能かつ再現性のある形で組織全体に展開できる。
wasm-bindgenのエクスポート削減と SAB + COOP/COEP 設定は、サイズ・レイテンシ双方で顕著な改善をもたらす。- Cargo の LTO と
opt-level = "z"は Acme Cloud でもデフォルト設定として推奨し、CI に自動ベンチマーク結果の可視化パイプラインを組み込むことがベストプラクティス。 - Edge 環境への WASI ビルドはコードベースの共通化を実現し、レイテンシ削減と運用コスト低減に寄与する。
次のステップ:本ハンドブックの手順をプロジェクトに適用し、Acme Bench‑Dashboard に結果をアップロードしてください。疑問点やカスタマイズ要望は #rust-wasm-opt チャンネルで共有しましょう。