はじめに
以下のイベントに参加しました!
本記事は随時更新します。時間があるとき、コツコツやります、、
セッション
セッションの内容に関しては、その際のメモと自分の解釈も含めてまとめます。参考程度でとどめてください。
イントロダクションとWasmの現状(@chikoski)
WebAssembly(Wasm)とは
高級プログラミング言語からコンパイルによって作成されるバイナリファイルのフォーマット。ファイルフォーマットだけでなく、プログラムなので仮想マシンの仕様だったり、それを使う命令セットが規定されている。
プログラムモジュールを定義し、他のプログラムに埋め込んで利用する。
図のように、importする関数のリストとexportする関数のリストがわかっている。
#[no_mangle]
pub extern "C" fn add(left: i32, right: i32) -> i32 {
left + right
}
#[no_mangle]
pub extern "C" fn sub(left: i32, right: i32) -> i32 {
left - right
}
import wasmtime.loader # noqa
import addtion # type: ignore
# Wasm ランタイムと、Wasm ファイルをロード
print("(+ 27, 6) =", addtion.add(27, 6))
# Wasm ファイルからエクスポートされた関数を呼び出し
RustでWasmになったモジュールをロードし、addtion.add
で呼び出しているのがコードから分かる。足し算はPythonが行っている訳ではなく、Rustから出力したWasmが行いPythonに返却している。
Wasmはバイナリで単なるファイルなので、動かすためにランタイムが必要。ランタイムは、特定の企業が出している訳ではなく、幾つかの主体が色んな用途で出している。
W3は、インタプリタで組み込み用途。
WebAssemblyの良さは?なぜ作ったのか?
良さはセキュリティ
と柔軟性
のいいとこ取りが出来る。
柔軟性
プログラムに何らかの自由度を入れるのには、幾つか方法がある。設定ファイルを書く方法
からアプリケーションを書く方法
まで自由度を入れる手段にはグラデーションがある。
設定ファイルを書く場合、設定ファイルを解釈する部分がセキュアに書けて入れば安全。柔軟性の観点でいうと、設計者が考えたことしかできない。そのため自由度は低いと言える。アプリケーションを書く場合、言語的な壁がある。
良く使われている方法は、プラグイン機構。Shopifyの決済のビジネスロジックなどをWasmでカスタマイズできる。昔はJavaScriptのランタイムだったが、Wasmになった。MSのフライトシミュレーターの振る舞いをWasmで開発できる。TODO: photshopとGoodNotesの事例
サーバレス環境でも利用されている。
- Istio ... サービスメッシュ
- envoy proxy ... Proxyの拡張をWASMで定義できる
- containerd ... Dockerやk8sのランタイムにコンテナを利用できる(要調査)
- runwasi ... 要調査
仕様の変化
仕様の決め方
仕様は、W3CというWebの標準化団体で行われている。Community Group
とWorking Group
の2つの主体がある。Community Group
で、技術の仕様が詰められて、ある程度詰めたらWorking Groupで文章化される。6段階ある。
Wasm の各プロポーザルの対応状況はこのページで分かるようになっている。
主要なランタイムとサポート内容が分かるようになっている。旗マークはフラグを立てれば利用することが可能。
Gabage collection
みんなが待ち望んでいる仕様の一つがWASMがGabage collection
をサポートすること。最近形になってきており、Java,Swift,Kotlin,Dartといった自分でメモリマネジメントしないようなGCに任せる言語がWASM対応するようになってきている。これまでも動いてきたように見えるが、GCのランタイムを自前でWASMにコンパイル時にくっつけていたが、バイナリサイズが大きくなるのという問題と、Webブラウザの中でKotlinで作ったオブジェクトはKotlinのGCで回収されるがブラウザ側のJSから握られているオブジェクトをどう管理する問題があった(GCできない)。この仕様で解決できるそうです。(Googleより)
コンポーネントモデル
コンポーネントモデルという仕様があり、WASMはモジュールを定義する仕様はあった。それをWarapコンポーネントという別単位を作る仕様で、なぜ必要かは最初は理解が難しい。実際にWASMモジュールを作って、intやfloatではない文字列型やユーザー定義型のデータを渡す、wasm-bindgenを使わずにやると必要な理由が分かるとのこと。一つのモチベーションは、WASMは関数の入出力とか関数をimportして、exportする書き方は決まっているが、受け渡されるデータの答えについては何も決められていない。何で作られいる言語かを意識しないと解釈できない。コンポーネントをどう表現するかバイナリフォーマットの仕様とIDLの仕様とユーザー定義型、文字列のような複雑な型を受け渡すための表現方法を定めた3つの仕様でできる。
インターフェース定義言語は以下のようになっており、この例では分数を表現している。s32(符号付き整数)を表す。分数を作るnew関数と、足し算するadd関数がある。これをツールに与えると自動的に雛形のコードを作ってこの関数の実装を実際に与えれば良い。出来たものをcargo-componentでコンパイルすると、WASMのコンポーネントができる。設定ファイルでリコンポーネントできる。WASMコンポーネントを配布するnpmやcrateのような配信する仕組みの使用も検討している。
package example
interface fraction{
record fraction { denominator: s32, numerator: s32}
}
world example {
use fraction.{fraction}
export new: func(denominator: s32, numerator: s32) -> fraction
export add: func(left: fraction, right: fraction) -> fraction
}
システムインターフェース
WASMは、関数はimport出来ます、exportしますしか決まっていないためプログラムが書けない。それはシステムコール、APIのセットが決まっていないため。これはあえてこうなっており、どんなランタイムで動作するか分からないため他で決めてくれという仕様。システムインターフェースはいろんな所で定義されている(envoy proxy
やabi
で定義例がある)が、埋め込み先のインターフェースを意識する必要があり、標準化してほしい。なのでWASMのWASIというサブグループを作り、そこでシステムインターフェースのセットを定めようとしている。
今のフェーズは、Preview1
が定められているPreview2
もインタフェースのセットの議論が終わってPreview3
を出して議論が終わったら標準化する流れ。Preview1は多くのランタイムがサポートするデファクト。Preview1
とPreview2
の違いは、Preview1は、結構ガッチリしたモノリシックなシステムインタフェースのセットで、POSIXのインターフェースのセットをそのままWASMの世界っぽく解釈したセット。Preview1
向けにビルドしてランタイムに与えるとCLIプログラムっぽく動く世界観が出来ている。Preview2
はコンポーネントモデルに従って、CLI用や、HTTPのプロキシで動くインタフェースといった単位で必要なAPIのセットを細かく砕いたものになる。まだアリー。
通して
仕様が進んできてGabage collectionが扱えるようになって、メモリマネジメントを頑張らなくて良い言語でも使えるようになってきた。
コンポーネントモデルがふわふわしているが、飛びつくのは怖いが、面白い。IDLが決まったのが大きい。IDLを書けば、CやTSのコード生成ができる。開発はしやすい。wasmtimeなどメジャーなランタイムがサポートしてきて、グルーコードを書かなくてもよくなりつつある。
システムインターフェースは標準化が進むといいな。Wasmerとそれ以外(ByteCodeAllience)が激しく対立している印象。