WebAssembly
https://scrapbox.io/files/673b4c0ef086f4eda218bc7d.png
https://developer.mozilla.org/ja/docs/WebAssembly
軽量で高速な バイナリ 形式の 命令セットアーキテクチャ
厳密には 仮想命令セットアーキテクチャ
仮想マシン + その 命令セット
https://webassembly.github.io/spec/core/intro/introduction.html#scope
MDN 曰く、現代の Web ブラウザ で実行できる新しい種類のコード
そのままだと人間には読めないので、WAT に変換する必要がある
当初は Web ブラウザ でのパフォーマンスの向上を目的として設計されていたが、ブラウザ以外でも利用できる
2015 年発表
構想自体はその前からあったっぽい radish-miyazaki.icon
https://ja.wikipedia.org/wiki/WebAssembly
2017 年に FireFox や Chrome をはじめとする主要なブラウザがサポートされた
現在のサポート状況: https://caniuse.com/wasm
2019 年に W3C(World Wide Web Consortium)にて仕様が策定
特徴
ビルドターゲットとすることが可能
これにより、様々な プログラミング言語 からビルドすることができる
事実、Rust や C / C++、Zig、Kotlin、Swift では、WASM ファイルを出力できるコンパイラが存在する
また、処理系を WASM としてビルドし、その上で実行するプログラムをデータとして WASM に追加することで、Ruby や JavaScript などのプログラムを WASM に変換するツールもある
様々なプログラミング言語でビルドできるため、たとえば C で書かれたプログラムをが持つ機能を利用した Web サイトを作ることが容易になった
WASM が登場する前は、JavaScript で C のコードを書き直す必要があった
warning.icon ただし、WASM を用いたとしても C 特有の機能や、同期的に書かれている部分を非同期処理に対応するように書き直す作業は発生するので、書き直しの労力が 0 になるわけではない
また、サードパーティのプログラムを拡張して実行するようなプラットフォームでも恩恵を受けられる
JavaScript で書かれた拡張のみを受け付けると、拡張は JavaScript プログラマしか実装できない
これを WASM にすると、プログラミング言語の選択肢の幅が広がり、その分多くのプログラマが実装できるようになる
具体例
Rust のビルドターゲットを WASM にしてビルドする
作成元が信用できないコードも安全に実行できる
WASM も JavaScript 同様、Web ブラウザで実行される場合はブラウザとは異なる環境(サンドボックス)で実行 される
この一端を WASM のメモリ構造から確認できる
WASM インスタンス(実行中の WASMファイル)からアクセスできるメモリを線形メモリと呼ぶ
線形メモリは単なるバイトの配列
セグメント方式 におけるセグメントのような概念は無い
そのため、スタック領域を不正に操作して、任意コードを実行する攻撃が難しい
コンパイラによっては、WASM の線形メモリの一部をスタック領域のように利用するものもある
この場合でも、WASM のコードは WASM インスタンスからアクセスできない領域に保存されており、実行可能フラグのようなものも線形メモリには無い
そのため、不正なコードをメモリ上にデータとして書き込み、実行可能フラグを設定することで実行する攻撃も難しい
また、システムが管理するリソースへのアクセスも制限 されている
リソースへのアクセスは システムインタフェース を介して行うが、システムインタフェースは Web ブラウザのような WASM の実行環境が用意している
そのため、 WASM の実行環境を用意する側で、WASM がアクセスできるリソースを制限できる
e.g. WASM CLI はプログラムを起動する API を持たない
そのため、shell のように他のプログラムを起動するには、WASM CLI とは別のシステムインタフェースを実行環境に用意する必要がある
バイナリ ファイルをテキスト形式(WAT)に変換でき、ソースコードとして読むことが可能
仮想マシンの概要
実マシンでプログラムを実行する場合
プログラムが扱うデータは インメモリ から レジスタ へ読み込まれる(ロードされる)
その後、レジスタに読み込まれた値に対して命令に応じた演算を行い、結果を必要に応じて メインメモリ に書き戻す
WASM の仮想マシンも概ね上記と同様
メモリからデータをロードして、ロードされた値に対して命令に応じた演算を行う
ただし、上記の場合にデータは スタック にロードされる
WASM の仮想マシンには レジスタ は無く、演算はすべてスタックを対象に行われる
メモリのアクセスは 1 Byte 単位で行う
WASM からアクセスできるメモリは最大 2GiB まで
なぜ 2GiB か?
メモリはページと呼ばれる 64 KiB(65536 Byte)で管理される
https://webassembly.github.io/spec/core/exec/runtime.html#page-size
このページのインデックス(アドレス空間)は 32 bit で表現される
https://webassembly.org/docs/portability/
https://webassembly.github.io/spec/core/exec/instructions.html#exec-memory-size
64 bit にする仕様が現在策定されている
https://github.com/WebAssembly/memory64/tree/main
もし 64 bit になると、wasm32 ではなく wasm64 というビルドターゲットが表示されるはず
そのため本来であれば 4GiB になるはずである
code:latex
2 ^ {32} = 4,294,967,296 Byte
しかし、ページインデックスの最上位ビットはエラー処理のために利用されるため、その半分の 2 GiB になる
ユースケース
『プラットフォームやプログラミング言語に依存しないビルドターゲットである』という特性を活かしたユースケース
マルチプラットフォーム 展開するため
採用例: Goodnotes
https://web.dev/case-studies/goodnotes?hl=ja
元々 Swift で書かれており、iOS と iPad 向けのアプリケーションを提供した
それを 2022 年に Web、ChromeOS、Android そして Windows 向けのアプリケーションをリリースしたが、このときに WASM(SwiftWasm)を採用した
なぜ WASM を採用したか
10 万行を超えるコードを再利用するため
コアプロダクトの開発が、クロスプラットフォーム アプリの開発に繋がるため
ビジネスロジック の移植や追加開発することなく、ある プラットフォーム へのノートが他のどのプラットフォームでも同じ描画となるようにすることができるため
あるプラットフォームで行われたパフォーマンス改善やバグ修正を、他のプラットフォームでも活用できるため
既存の資産を生かしたWeb サービスを提供するため
幅広い開発者に開発やエコシステムに参加してもらうため
『作成元が信用できないコードも安全に実行できる』という特性を活かしたユースケース
アプリケーションの拡張機能
機能拡張の問題点
機能拡張が必要ということは、実現したいことが設定ファイルで記述できる範疇を超えて多様であることを意味する
そのため、拡張機能を有効にすると、何らかの別のプログラムが動作する
ここで問題になるのが、動かしたプログラムが安全であるか どうか
もし、開発元に認定された開発者だけが機能拡張を作成できるように制限した場合、プログラムが安全であることを確認することは、そうでない場合と比較して容易である
しかし、第三者が機能拡張を作成できるようにした場合はその限りではない
WASM を用いると、上記の問題を解決できる
WebAssembly#670f687375d04f000021c96c
WASM の実行環境はサンドボックス内部で WASM にビルドされたプログラムを動作させるため
その他のメリット
ビルドターゲットとできる点も機能拡張の多様さや開発者の確保という点で有利に働く
ライブラリとして提供されている WASM の実行環境も用意されている
これにより、1 から実行環境を実装しなくても、WASM を利用した拡張機能をサポートできる
採用例
Microsoft Flight Simulator
https://docs.flightsimulator.com/html/Programming_Tools/WASM/WebAssembly.htm
WASM のアプリケーションの機能拡張として WASM を採用
Shopify Functions
https://shopify.dev/docs/apps/build/functions/programming-languages/webassembly-for-functions
サービスの機能拡張として WASM を採用
EC サイトでのカスタム ビジネスロジック の実現
サーバレス 環境に配置する処理の実現
JavaScript よりもパフォーマンスが安定することを期待して利用するケースもある
Web ブラウザの搭載する JavaScript の処理系(e.g. V8)は、実行時に適用される 最適化 手法や段階によってパフォーマンスが異なるケースがある
一方、WASM はパフォーマンスのばらつきが少なく、JavaScript よりも安定したパフォーマンスを発揮する
warning.icon WASM 登場当初は、JavaScript よりも高速である点がメリットとして挙げられていた
しかし、JavaScript 処理系の改善に加え、線形メモリと JavaScript のオブジェクトとの間でデータコピーが発生するぶん WASM のほうが処理速度が遅い ケースもある
そのため、2024 年 10 月現在では、処理速度よりも安定したパフォーマンスのほうに着目されることが多くなった
標準化プロセス
標準化は W3C で行われる
最終的に標準化を行うのは WG(Working Group)で、仕様の議論そのものは CG(Community Group)で行う
これは、仕様の策定ができるのは WG に限られるという制約があるため
WG は企業が主なメンバーになっているのに対して、CG は個人も多く参加している
仕様の標準化は以下の 6 つのフェーズがある
https://github.com/WebAssembly/proposals
Phase 0 - Pre-Proposal (CG)
CG に属する個人が WASM に追加したい仕様のアイデアを、GitHub リポジトリの Issue として登録する
Phase 1 - Feature Proposal (CG)
Pre-Proposal のうち、CG から広く興味を得た提案
提案が CG のスコープに含まれることや実現性を検討する
Phase 2 - Proposed Spec Text Available (CG + WG)
仕様を明文化する
Phase 3 - Implementation Phase (CG + WG)
仕様を実装する
これは、標準化するためには 2 つ以上の実装が必要となるため
実装をもとに再度仕様を再検討するケースもある
Phase 4 - Standardize the Feature (WG)
仕様の標準化
WG 内で議論する
Phase 5 - The Feature is Standardized (WG)
W3C の勧告として標準化
追加された仕様の例
複数の戻り値を返す 多値関数
メモリ領域に対する操作
固定サイズの SIMD
上記のような 命令セット の追加のような 仮想マシン そのものを拡張する 以外 にも存在する
e.g. JavaScript BigInt to WebAssembly i64 integration
JavaScript に存在する BigInt と WASM の符号付き 64 bit 整数との相互変換方法を定めたもの
e.g. Component Model(後述)
コンポーネントモデル(Component Model)
長くなったので別ページに切り出した radish-miyazaki.icon
WebAssembly Component Model
参考
Rustで学ぶWebAssembly ―入門からコンポーネントモデルによる開発まで
https://qiita.com/kawamou/items/7c56827e1e9a4795c026#wasmの仮想マシン
https://qiita.com/wbcchsyn/items/3c9f331cefbd139a239e
https://www.ipsj.or.jp/dp/contents/publication/51/S1303-S03.html