dackdive's blog

新米webエンジニアによる技術ブログ。JavaScript(React), Salesforce, Python など

Software Design 2021年3月号の「WebAssembly入門」を読んだ

rust-jp の Slack で知り、読んでみました。
3 ~ 4 時間でさらっと読めるボリューム。

全体的に手を動かす成分はそこまで多くなかった印象で、読み物として面白かった。
以下、各章の感想など。

第1章 WebAssemblyの登場の経緯と利用方法

自分のこれまでの知識を復習しつつ、歴史的経緯などは知らなかった部分も多かった。
WAT のフォーマットについては以前読んだこちらの記事が良かった。

WebAssemblyハンズオン: 実際に動かして基礎を学ぶ(翻訳)|TechRacho(テックラッチョ)〜エンジニアの「?」を「!」に〜|BPS株式会社

また、印象的だったのはWasmの用途のところで

現代のブラウザが JavaScript を高速に実行するためには、大まかに「(最適化をあまりしないので)早く実行開始できるけど実行速度は遅い処理系」や「実行速度は速いけど(最適化に時間がかかるので)実行開始までが遅い処理系」といった、複数の方式の処理系を使い分けることが多いです。その関係で実行速度を制御しづらく、ブレが生じやすいのです。一方で Wasm はそうした実行時の最適化をあまり伴わない分、処理速度が比較的安定していることも、リアルタイム処理に採用される一因なのではないかと推測されます。

という記述があったが、Wasm は処理速度が安定しているという話は以前 Front-End Study でも聞いたなあ。

www.youtube.com

f:id:dackdive:20210324001919p:plain

(動画 1:14:00 あたりから)

第2章 Wasmで広がる Webアプリの可能性

元々多言語で作られたものを Wasm を通して Web アプリ上で動かす、の例として FFmpegImageMagick のデモが紹介されていた。
コードもあってわかりやすい。

第3章 WASIとProxy-Wasm

Wasm をブラウザ外で使うために定められた仕様の話だったり、ブラウザ外 Wasm の一例として Proxy-Wasm やそのリファレンス実装である Envoy の話など。

Proxy-Wasm 気になってたので概要だけでも理解できてよかった。

ちょうど先週末の Kernel/VM 探検隊 というイベントでも関連セッションがあったみたいで、スライドも見た。

www.slideshare.net

あと記事の著者は昨年末の WebAssembly night #10 - connpass でも Proxy-Wasm について登壇されてた。(観てなかったの思い出した)

メモ

読みながらとったメモ。

## 第1章

- Wasmの起源は asm.js (2013年3月 Mozilla)
    - JSには任意のオブジェクトに対しビット単位演算子OR `|` を適用すると強制的に整数に変換するという特性がある
    - これを生かして、 `foo | 0` は整数型の式とみなす、などのルールを設けることで、JSを静的型付け言語となるかのように制限する
    - このような前提を設けることで、Cなどのプログラミングからasm.jsに変換する

- asm.js はJSと高い互換性があるので多くのブラウザでサポートされた一方、プリミティブな命令群を生成する関係上コード量が増える、増えると構文解析に時間がかかるといった問題があった
- Wasmはasm.jsの問題を解決
    - バイトコードでサイズを小さく構文解析が簡単に
    - プラス、より安全にするためにJSの環境から分離させる
- WAT
    - `wat2wasm` WAT → Wasm 変換ツール。WebAssembly Binary Toolkit に含まれる
    - S式: `(宣言 .... 宣言の内容....)`
- WasmはJSを置き換えるか→NO
    - 現代の多くのプログラミング言語は重厚なランタイムが必要で、それをWasmにコンパイルするとサイズが大きくなる
    - Wasm からブラウザの API を直接呼ぶことはできなずJSのAPI経由で間接的に呼ぶことになる。ので Wasm ←→ JS 間の呼び出しオーバーヘッドがある
    - Wasm の公式見解としても、Wasmがコアのロジック担当、JSがそれとHTML/CSSベースのGUIとの橋渡し役になる、という役割分担を想定して仕様を策定しているらしい
- Wasm は速度が安定しているみたいな話
    - JSは「(最適化をあまりしないので)早く実行開始できるけど実行速度は遅い処理系」や「実行速度は速いけど(最適化に時間がかかるので)実行開始までが遅い処理系」といった、複数の方式の処理系を使い分けることが多い。その関係で実行速度を制御しづらくブレが生じやすい
    - Wasm meetup で聞いたかも
- Wasm 活用事例
    - Unity は 2018.1 で Wasm 対応
    - C言語からの移植
        - FFmpeg
        - md4c というmarkdown parser

### Wasm に触れる

- wasm-pack: Rust で作成したWasmのモジュールをフロントエンドやNode.jsなどのプロジェクトで使いやすいようビルドしてくれるツール
    - コンパイルしたWasmモジュールやwasm-bindgenが生成したラッパーをnpmパッケージとして使えるようにしてくれる
- wasm-bindgen
    - Wasm で定義した関数は通常整数や浮動小数点数といった単純な値しかやり取りできない
    - 文字列や構造体、配列といった値を扱うには「線形メモリ」と呼ばれるWasmモジュール専用の領域に書き込む必要がある
    - そのへんの面倒を担ってくれるラッパーを生成する
- "`crate-type = ["cdylib"]` は、このプロジェクトが動的リンクされるライブラリとしてビルドすることを宣言している"
    - 🤔
- `wasm-opt` (wasm-packが内部で使ってる)
    - [https://github.com/rustwasm/wasm-pack/issues/886](https://github.com/rustwasm/wasm-pack/issues/886) は wasm-bindgen v0.2.70 で fixed
        - "エクスポートする関数が文字列を返す関数となっていた場合、これが必要ですのでご注意ください。"

## 第3章 WASIとProxy-Wasm

- なぜブラウザ上でWasmが動くの?
    - Wasm そのものはスタックベースのVMおよびそこで実行されるバイナリの仕様で、ブラウザとは関係ない
    - ブラウザがWasmのVMを実装しているので動かせる、というわけ
    - Wasmの実行環境のことを「ホスト環境」と呼ぶ
- WasmはどうやってJSやブラウザと連携してるの?
    - Wasm の仕様にある Import section, Export section, Host function がカギ
    - Import section
        - Wasm のバイナリの一部
        - そのプログラムに対して外部から提供されるリソースを記述したもの
        - 例:グローバル変数や関数
        - import 時にJS側で渡すやつかな
    - Export section
        - 同じくバイナリの一部
        - プログラムの中でほかのWasmのバイナリやホスト環境で利用可能なものを記述したもの
        - `#[wasm_bindgen]` つけた関数とかかな
    - Host function
        - ホスト環境側で定義した関数を Wasm にリンクした際のオブジェクトのこと

    ### WASI

    - 複数のホスト環境それぞれに共通する、システムコールに対応するHost functionの仕様を策定したもの
        - 例:Wasmのプログラムからファイルを読み書きしたり、現在時刻を取得するためのHost function
    - WASI ランタイム:WASIを実装した、Wasmを実行可能なホスト環境のプログラム
        - Lucet, Wasmtime, Wasmer, WAVM, SSVM など
    - Wasm の応用例:Krustlet
        - Kubernetes上でコンテナとしてWasmのバイナリを実行する環境を提供するプロジェクト
        - "通常のコンテナを利用する場合と比べ、WASI を利用してユーザーのプログラムを実行した場合、システムコールはプロセスが走っている OS カーネルではなく WASI ランタイムのレイヤに発行されるため、プロセスとしての分離性が高くよりセキュアであると考えることができます"

### Proxy-Wasm

- 背景となるEnvoy Proxy
    - オープンソースのプロキシサーバ
    - Service Mesh(ざっくり言うと、マイクロサービスのネットワーク通信をプロキシ経由にする)のData Plane(そこで使われるプロキシ)
- Envoyはプロキシサーバなので個別ユースケースが無数に存在する
    - そのためプラグイン機構が存在するが、以下のような課題もあった
        - C++ で開発しなければ いけない」「Private なユースケースの場合は、 Envoy の Upstream へマージすることはできない ので、ユーザーが独自ビルド&メンテナンスを しなければいけない」「拡張機能追加後にすべて の Envoy のプロセスを入れ替えなければならな い」
    - "「複数言語をサポート」「Envoy 自体の独自ビ ルドは必要ない」「Envoy のプロセスを止めるこ となく動的にプラグインを Load 可能」「堅牢な セキュリティ」などの要求を満たした拡張機構の 開発に乗り出しました。"
- Proxy-Wasm
    - Wasm のプログラムによりプロキシを拡張するための VM とホスト環境の間の ABI の仕様策定やSDK開発を目的としたプロジェクト
    - WASIと独立しているが、実質的にWASIの一部APIと各種プログラミング言語のWASI向けのツールチェインに依存しているため、WASIのサブプロジェクトになるのではという話もある
- Proxy-Wasm の仕様と実装
    - 双方向的なのが特徴。WASIはWasmの中からホスト環境へ発行するHost functionの仕様なので一方向
    - 例:VM→ホスト環境
        - `proxy_send_http_response`
    - 例:ホスト環境→VM
        - `proxy_on_request_headers`
- Proxy-Wasm の課題
    - ホスト環境の実装の難しさ
        - Wasm のプログラムによってホスト環境がクラッシュしないようにしないといけない
        - 悪意のある Wasm バイナリがロードされるかもしれない
    - パフォーマンス:50%ほどネイティブと比較して遅い
    - VMの実装の選び方