😺

WebAssembly 入門してみた

2022/11/13に公開

WebAssembly 入門してみた

最近こんなニュース記事を見て、
WebAssemblyに対してクラウドサービスを抽象化、そのままAWSでもAzureでもGoogle Cloudでも実行可能にする「SpiderLightning」、Deis Labsが公開、標準化も推進
いろんなOSにまたがった実行環境という意味ではJavaのJVMみたいだなと思ったり(Javaそんなに詳しくないけど)、「WebAssemblyってそんなこともできるのか...」とちょっと興味を惹かれた。
名前のちょっとした概要だけは知っている所謂「にわか勢」だったので、良い機会だし少しちゃんと調べてみる。

そもそもWebAssemblyて

とりあえず公式ページを見てみると、こんな記載がある。

WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine.

WebAssembly ==「バイナリのフォーマット」 で、そのフォーマットに従ったバイナリを実行する 仮想的なスタックマシン があるみたい。
やっぱりJVMとJava Bytecodeの関係にそっくりな気がしてきた。(Javaそんなに詳しくないけど)
Wasmと略すらしい。

続いて、4つの特徴が記載されている。

Efficient and fast

The Wasm stack machine is designed to be encoded in a size- and load-time-efficient binary format. WebAssembly aims to execute at native speed by taking advantage of common hardware capabilities available on a wide range of platforms.

まずは効率的、かつ高速であること。
どうやら、Wasmスタックマシン自体がサイズも小さく効率的に読み込めるようなバイナリになるように設計されているらしい。(脳死)
いろんなプラットフォームでネイティブコード並の実行スピードの実現を目指しているらしい。

Safe

WebAssembly describes a memory-safe, sandboxed execution environment that may even be implemented inside existing JavaScript virtual machines. When embedded in the web, WebAssembly will enforce the same-origin and permissions security policies of the browser.

安全性。
Wasmはサンドボックス環境で実行されるためメモリ安全性が保たれているらしい。(脳死)
ブラウザに組み込まれる場合、Same-origin policyやブラウザのセキュリティポリシーにも従うとのこと。(CORSとかも有効になるということ?)

Open and debuggable

WebAssembly is designed to be pretty-printed in a textual format for debugging, testing, experimenting, optimizing, learning, teaching, and writing programs by hand. The textual format will be used when viewing the source of Wasm modules on the web.

デバッグの容易性。
とにかく人間に見やすいように出力してくれるみたいで、デバッグとかテストとか最適化とか学習とかいろいろとやりやすいらしい。

Part of the open web platform

WebAssembly is designed to maintain the versionless, feature-tested, and backwards-compatible nature of the web. WebAssembly modules will be able to call into and out of the JavaScript context and access browser functionality through the same Web APIs accessible from JavaScript. WebAssembly also supports non-web embeddings.

最後はWebのプラットフォームの一部としてもデザインされているとのこと。
ようするに、ブラウザ上で実行されるJavaScriptみたいに、ブラウザのAPIにアクセスできるようにいろいろと用意されているらしい。
なお、ブラウザ以外の環境への組み込みにも対応しているとのこと。(e.g. Node.js上のJavaScript VMとか)

Webブラウザに限った話じゃなさそう?

WebAssembly」って名前だけど、見た感じWebブラウザに限った話ではなさそう。
(まあ調べるきっかけになったニュース記事からもそんな雰囲気はバンバン出てるし、non-web環境への対応もしてるって書いてあるし)

本質的には、 「いろいろ工夫された独自のバイナリフォーマット」+「そのバイナリを実行可能なスタックマシン」 がセットで、このスタックマシンが動かせる場所であればどこでも実行可能ということなのかも。(ブラウザ上だったり、ホスト上だったり)

Wasmの中間表現

主要なコンパイラとかでは、最終的なバイナリを吐き出したりする前に一時的な出力として「中間表現」という形のデータ構造を経由することがよくある。
具体的には、中間表現を生成するフロントエンドコンパイラと、中間表現を読み込んで実行したりネイティブコードに変換したりするバックエンドコンパイラに分かれていたりする。
LLVMとかが有名な例。

Wasmでは、内部的にこの中間表現にS式を利用しているらしい。(主にLispとかで見られる (カッコだらけのやつ (Emacs使ってるとS式よく扱うのでなんとなく親近感)))

主要な概念

公式に記載がある主要な概念としてはこんな感じ。

Values

Wasmで扱う値。4種類の基本的な数値型が提供され、それぞれ32bit / 64bit幅が存在する。
32bitの数値型は、メモリアドレスやブール値としても使用される。符号付きとか無しとかは区別しない。
追加で128bit幅分のベクタ型が提供されていて、この幅に収まるようにいろんな組み合わせでまとめられる。(32bitを4個とか、8bitを16個とか)

Instructions

Wasmの命令。並べられたバイナリの命令が上から順番に実行される。(アセンブリ言語と同じ)
複数の命令が順番に並べられたものを「コード」としている。
スタックマシンなので、基本的にメモリ上のスタックに値を突っ込んで取り出す。
関数への引数の受け渡しもスタックにpushだし、計算結果の受け渡しもスタックから引数をpopして結果をpushする。

Traps

実行中に何かしらの異常があるとトラップが生成され、即座にWasmの実行が止められる。
Wasmのコード側(スタックマシンの内部)からはキャッチできず、スタックマシンの外側の環境(実行している環境)でキャッチできる。
Linuxカーネルのシグナルとかトラップとかのそれに近しいやつ。

Functions

コードを分割して再利用可能にした単位。
Function内では、仮想的なレジスタとしてローカル変数(可変)を定義できる。

Tables

値や関数を関節参照するためのテーブル。テーブル内に値や関数へのポインタをインデックスとして登録しておけば、型がはっきりしてない値にアクセスしたり、実行時の環境に合わせて処理を差し込んで動的に参照したりできる。
LinuxのELFフォーマットでいうところの、GOTとかPLTみたいな役割?

Linear Memory

実行中に動的にサイズ変更可能なメモリ領域。アドレス指定で値を読み書きできる。
ヒープ空間に近い感覚。

Modules

Wasmバイナリからimportできる形に定義されたWasmバイナリ。
いろんなプログラミング言語で見られるモジュールの概念とだいたい一緒。

Embedder

Wasmの実装が組み込まれるホスト環境のこと。
どんな風にモジュールを読み込んだり、モジュールを内部のコードに提供したりするか定義されている。

WASI

WebAssembly System Interface の略。
いろんなOSの機能へのアクセスを抽象化して Wasm から利用できるようにしたAPIらしい。(ファイルシステムとかソケットとか)

WebAssembly is designed to run well on the Web, however it's not limited to the Web.

WASIのドキュメントにも「Webに限った話じゃないよ」て書いてある。

主要なランタイム

対応状況としてはこんな感じ。

各種ブラウザ

主にChromiumベースのブラウザ、Firefox、Safariはだいたい対応している様子。

Wasmer

Rust製。WASIにも対応してる。
WebAssemblyをサーバー上でも動かそうというプロジェクト。

Wasmer関連のプロジェクトには他にもこんなのもあるらしい。

  • WAPM
    • WebAssembly Package Managerの略
    • wapm.ioっていうパッケージレジストリに保存されたWAPMパッケージをインストールしたり管理するWasmモジュールらしい
    • ちらっと見てみたら、spidermonkeyとかirbとかsqliteとか結構主要なパッケージも対応してた
  • WebAssembly.sh
    • Wasmer-JSで書かれたWeb上で使えるシェル
    • Wasmモジュールをドラッグ & ドロップで読み込めるらしい

Wasmtime

これもRust製。WASI対応。
Bytecode Allianceという、WebAssemblyをいろんな環境でセキュアに動かそうという団体のプロジェクトらしい。

ちなみにWASIの仕様を策定しているのもこのWasmtimeのプロジェクト。

WasmEdge

C++製。WASI対応。
CNCFのプロジェクトらしい。(Landspace上では Container runtime に分類されてた)

WasmEdgeのドキュメントに具体的なユースケースがいくつか書いてあった。

  • ブラウザやクラウド上のWebアプリケーションとして。(ReactやVueなどのクライアントサイドとの連携や、SSRなどのサーバーサイドの処理なども)
  • サーバーレスなFunctionとして (AWS LambdaのFunctionとか)
  • マイクロサービスの実行環境として (Kubernetesみたいなオーケストレーターとか、Daprみたいなフレームワークとか)
  • スマートデバイスのネイティブコードとして
  • とかとか...

試してみた

GitHubのスター数が一番多いのが今の所Wasmerのようで、とりあえず長いものに巻かれていくスタイルでランタイムは Wasmer を選択。

Wasmerインストール

$ curl https://get.wasmer.io -sSfL | sh

.bashrcとかに設定が追加されたり、$HOME/.wasmerが作成されたりする。
wasmerが使えるようになる。
※ libncurses5とかがインストールされてないと共有ライブラリがなくて怒られるので注意

とりあえずハロワ

fn main() {
    println!("Hello Wasm!");
}

いろいろやる

# ビルドターゲットを追加
$ rustup target add wasm32-wasi

# wasmにビルド
$ cargo build --target wasm32-wasi

# 色々吐き出されている
$ ls ./target/wasm32-wasi/debug
build  deps  examples  hello-wasm.d  hello-wasm.wasm  incremental

# wasmerで*.wasmファイル実行してみる
$ wasmer ./target/wasm32-wasi/debug/hello-wasm.wasm
Hello, Wasm!

これでWasmランタイムが動く場所があればどこでも動かせるという感じ。
今回はWASIを経由してホスト上で実行しているが、ブラウザのAPIと繋ぐように書けばアラート出したりとかできるんだろう。

最後に

ちょっと長々と書いてしまいましたが、とりあえずざっくりと概要を流しみたところ、自分のこれまでの理解が概要の概要にも満たなかったことを認識しました。
色々調べてみて面白そうだったので今後もWatchしていきたいと思います。(今更感)
RustのYewとかも気になってる。

Discussion