🌐

なんとなくわかって使えるWebAssembly

2024/03/29に公開

最近、よく見かけるWasm(WebAssembly)がどういうものなのか、どうやって使うのかについてざっくり理解できる記事を書いていこうと思います。
この記事がWasmを調べるきっかけになると幸いです。

Wasm概要

まず、安心のMDNへ

WebAssembly は現代のウェブブラウザーで実行できる新しい種類のコードです。ネイティブに近いパフォーマンスで動作する、コンパクトなバイナリー形式の低レベルなアセンブリー風言語です。さらに、 C/C++、C# や Rust などの言語のコンパイル先となり、それらの言語をウェブ上で実行することができます。 WebAssembly は JavaScript と並行して動作するように設計されているため、両方を連携させることができます。

参考 MDN Web Docs

Webブラウザでネイティブに近いパフォーマンスで動作できるバイナリ形式のコードらしいです。
ブラウザで高速なゲームなどができるようになるとしたら魅力的ですね。

ただ、デメリットもあります。

  • ビルドサイズが大きくてページの読み込みに時間がかかる点
  • Domの操作もできるがJSから直接操作するより遅い点
    ...

などは注意が必要です。

うまく、JSとWasmを組み合わせることで、よりハイパフォーマンスなアプリケーションをWebブラウザから提供することができます。

例えば、ブラウザ上で共同編集できるプロトタイピングツールのFigmaはwasmを使用しています。
https://www.figma.com/ja/blog/webassembly-cut-figmas-load-time-by-3x/

また、

  • いろいろな言語からwasmにビルドすることができる

という点も大きなメリットの一つです。

複数の言語からWasm化できることでそれぞれの言語の強みやエコシステムを使うこと(工夫が必要な場合もある)ができるようになります。
JSだと比較的難しいメモリの管理もRustを使うことでより簡単に行うことができます。

Wasmの実行手順(Rust↔︎JS)

  1. ビルドターゲットをwasm32-unknown-unknownでRustでコードを書く
  2. wasm-packなどツールを使ってRustからwasmへコンパイル
  3. JS側からWasmをインポート

Wasmをjsから実行するためのおおまかな流れは以上のようなフローです。

具体的な実装方法の説明は以下の記事をご覧ください!
https://developer.mozilla.org/ja/docs/WebAssembly/Rust_to_Wasm

実装時に気をつけていること

型の共通化

JS(TS)からWasmの関数を呼び出す際にWasm化する時に.d.tsにRustの型を書き出し、RsutとTS間で型に差異がないように工夫しています。

Wasm↔︎JS間の値の受け渡し時のオーバーヘッド

Rust↔︎Wasm↔︎JS(TS)の型を変換するためにオーバーヘッド(値のコピー)が起きます。
小さいデータならそこまでセンシティブにならなくてもいいかもしれませんが、幾何データなどあまりに大きいデータをやり取りする場合は注意が必要です。

対処法

Wasmのデータは線形メモリに配置されます。
そのため、Wasmからメモリアドレスと長さをJSに送り、JSからArrayBufferを使用してWasmの値にアクセスすることができます。

以下の記事に実装方法が記載されているので参考にしてみてください。

https://zenn.dev/a24k/articles/20221107-wasmple-passing-buffer
https://qiita.com/Kanahiro/items/1894ceebc49cd48391c5

Wasm↔︎JS間のメモリのやり取りについて

全てをWasmで完結しようとしない

WasmはJSを置き換えるものではないと思っています。
WasmとJSでそれぞれの得意・不得意があるので適材適所、要件にあった使い方ができるように心がけています。

Wasmサイズの最適化

Wasmはビルド後のサイズが大きくなってしまうことが一つの課題です。
色々な最適化手法があるので試してみましょう。

https://book.leptos.dev/deployment/binary_size.html

エコシステム

wasm-pack

Rustからwasmを生成してくれるツールです。JSのファイルなどもまとめて生成してくれます。

https://rustwasm.github.io/wasm-pack/book/

Tsify

簡単にRustの構造体からTSの型を.d.tsに書き出してくれるクレートです。

https://github.com/madonoharu/tsify

wasm-bindgen

WasmモジュールとJS間のやり取りをしやすくしてくれるクレートです。

https://rustwasm.github.io/wasm-bindgen/

Wasi

ウェブブラウザ以外でwasmを使うためのインターフェイスです。
(あまりよく知らない…)

以下の記事がわかりやすかったです!
https://inzkyk.xyz/mozilla_hacks/wasi/

WIT

各言語で書かれたコードをWasm化すると各言語の型がWasmの型(基本的には整数と浮動小数点数のみ)に変換されます。各言語間の型の違いを補完し、安全にwasmを実行するためのインターフェースです。
参考

オーバーヘッドも起こりにくいように工夫されています。
詳しくは以下の記事を参考にしてください。
https://inzkyk.xyz/mozilla_hacks/webassembly_interface_types/

最後に

Wasmまわりはまだまだ日本語の情報が少ないかもしれません。
Rustやwasm周りの情報は公式ドキュメントがとてもわかりやすく記述してくれているのでながめてみると面白いです!
ブラウザから以外の使い方もされているので個人的に今後も追っていこうと思います!

Discussion