この記事は Google I/O '19 のセッションの視聴メモです。
想定読者は自分なので正確性や網羅率には問題があるかもしれません。
Reference
Speakers
- Surma Surma
- Deepti Gandluri
Abstract
WebAssemblyは既存のC++のコードベースをWebにもたらすパフォーマンスのためのツールとして認識されています。そういったタスクは例えばゲームなどですが、WebAssemblyのちからはそれにはとどまりません。WebAssemblyはJavaScriptのボトルネックを外科的に解消するためのパズルのピースなのです。
Contents
Introduction
Webで見つかるWebAssemblyのコードはC++とEmscriptenによるものがほとんどだが、C++だけがWebAssemblyを記述するための言語ではない 1
Emscripten
- QTがWebAssemblyに対応したのでブラウザ上ででネイティブっぽいGUIが動くようになった
- QTはどうやってWebAssemblyに対応したのか → Emscripten
- EmscriptenはCコードをasm.jsに変換できる(WebAssemblyにも変換できる)
- Cのフィアル操作やOpenGLのコードはそれぞれ対応するWebのコードに変換される(WebGLなど)
- WebAssemblyはJSコードに慣れ親しんだWeb開発者に使えるか?C++書きたい?
- NO
- 両方使えるエンジニアが少ないのでWebAssemblyはとてもニッチな技術になってしまっている
- squoosh.app でやったことは一つの解決策かも知れない
- JSで実装された画像処理ライブラリはC++で実装されたものに比べ少ないのでC++で実装されたmozjpegをWebAssemblyに変換して利用した
コンパイル方法
$ cd node_modules/mozjpeg
$ autoreconf -fiv
$ emconfigure ./configure --without-simd
$ emmake make libbjpeg.la
- JSもwasmもSIMDには対応していないので
--without-simd
オプションが必要 - wasmコードをJSコードで呼び出すためのコード↓
ブリッジコード
#include "jpeglib.h"
val encode(
std::string image_in,
int image_width,
int image_height
) {
uint8_t* image_bubffer = (uint8_t*) malloc();
return val(typed_memory_view(size, image_buffer));
}
EMSCRIPTEN_BINDINGS(mozjpeg_wasm) {
function("encode", &encode);
}
wasm-pack
- RustをWebAssemblyに変換するツール
- squoosh.app では画像のリサイズに利用している
ブリッジコード
extern crate resiez;
extern crate wasm_bindgen;
#[wasm_bindgen]
pub fn resize(
mut input_image: Vec<u8>,
input_width: usize,
input_height: usize,
output_width: usize,
output_height: usize,
) -> Vec<u8> {
// Use "resize" crate
return output_image
}
コンパイル
$ wasm-pack build --target web
wasmのパフォーマンス
- squoosh.app では4つのC++/C/Rustライブラリをwasmに変換して利用している
- wasmはJSより速いのか?
- ピーク性能は同じ
- wasmの方が速い速度で安定しやすい
次の画像はV8の中でJSとwasmが同実行されているかという図
- まず明確な違いはIgnitionはインタープリターでLiftoffはコンパイラだということ
- IgnitionとTurboFanの間は互いにデータがやり取りされているように見えるが、それはIgnitionがTurboFanのマシンコードの実行を監視してコードの最適化に必要な情報を得ているから
- その情報が役に立たないこともあるが、その場合は無駄なことをやってしまったことになる
次の画像はJSの実行時間とwasmの実行時間を並べたもの
- wasmが速いのは重要だが、それ以上に重要なのはwasmはJSに比べて実行時間が非常に安定しているということ
AssemblyScript
- TypeScriptでwasmを出力できるコンパイラ
- 新しい言語を学ばなくていいのが嬉しい
- メモリを使用するための
load
やstore
といった組み込み関数が使える - JSではないのでガベージコレクタはない
- 次のような感じで自分でメモリ管理をしないといけない
import "allocator/tlsf";
let ptr = memory.allocate(64);
// do something ...
memory.free(ptr)
Future Of WebAssembly
Threads
- CやC++で書かれたマルチスレッドで実行されるものがあるのでwasmでもThreadは必要
- WebAssemblyでのThreaddingはWebWorkerを用いて実装されている
- 通常のマルチスレッドプログラミングはメモリを共有しているがWebWorkerではどうするか
- WebWorkerでのメモリ共有は限定的
- すでにChrome 74でリリースされている
WebWokerを利用したマルチスレッドプログラミング
SIMD Extension
- 1つの命令に複数のデータを適用し並列実行を実現するための命令セット
- image / audio video codecs / Google Earth / Photoshop / machine learning 等に使える
WebIDL Bindings
- wasmから直接Web APIを呼べるようにする
- 今まではJSでブリッジしていたのでオーバーヘッドが発生していた
その他
- ガベージコレクション
- 末尾再起最適化
- エラーハンドリング
所感
- wasmを利用するためのツールとしてEmscripten,wasm-pack,AssemblyScriptなどが紹介された
- 次のような使い分けになりそう
- 既存のコードベースをWebで利用したい → Emscripten
- WebAssemblyのコードを1から書きたいJSエンジニア → AssemblyScript
- WebAssemblyのコードを1から書きたいシステムエンジニア → wasm-pack
- wasm-packのコードがEmscriptenのコードと比べてみてもかなりシンプルでいいと思った
- AssemblyScriptは学習コストを抑えてwasmが書けるのはいいがメモリの管理を自分でやらないといけないのは辛い
- ガベージコレクションが実装されたらかなり書きやすくなりそう
-
2019年3月にはLLVMがWebAssemblyに公式に対応した ↩