フロントエンドとしては絶対に避けて通れないWASM、そろそろエッジ環境なら試せるツールが揃ってきたということで、手を出してみた。
自分がどうしてもローレベルに弱いので、たぶん色々間違ってるんだけど、識者各位は指摘お願いします。
現在の仕様はここ https://github.com/WebAssembly/spec
Chrome Canary と Firefox Beta で動作可能
WASMのゴール
- asm.jsに比べてサイズが小さく高速にデコード可能なバイナリフォーマット
- 将来的な目標として、DOMアクセスとそのGCインテグレーション
- これが達成されれば、ブラウザにおいて JS がファーストプライオリティな言語という状態ではなくなる
- とはいえ既存の資産が多いのでなくなることはないだろうが
- wasmのスペック自体は小さいのでおそらくブレようがないが、DOMの実装とGCは各ベンダごとに別々なので、おそらく困難になるであろうという見通し
- これが達成されれば、ブラウザにおいて JS がファーストプライオリティな言語という状態ではなくなる
サポートされたからといって、今すぐにjsを置き換えるものではない。DOMとGCのインテグレーションできれば将来的にそうなる可能性はある。
一般的には clangのような llvm フロントエンドから llvm-ir を通してwasmバイナリが生成される。wasmの中間仕様は明確で、可読性の高いS式なので、それを直接コンパイルすることもできなくはない。wasm専用コンパイラになるが。
ビルドするソースが全てllvmで統一され、wasmの要求するいくつかの仕様に従っていれば、理論上は全てwasmにコンパイルできるので、rust や swift, あるいは LLILC上のC# も将来的に wasm にコンパイルできるようになる可能性は高い。現在は無理。
WebAssembly関連のツールチェイン
- kripken/emscripten
- c(++) => asm.js
- WebAssembly/binaryen
- asm.js or アセンブリ => wast
- WebAssembly/sexpr-wasm-prototype
- wast => wasm
kripkenはasm.jsやwasm界隈だと頻出人物なので動向をwatchしよう
http://ast.run/ では wasm(というかwast) をブラウザ上で書いてテストすることが出来る
拡張子とそれぞれの状態
- 拡張子
.ll
- 生成例
$ clang -emit-llvm foo.c
- llvm-irの中間状態
- 生成例
- 拡張子
.s
- 生成例
$ llc -march=wasm foo.ll
- アセンブラのソースコード
- 生成例
- 拡張子
.wast
- 生成例:
s2wasm foo.s > foo.wast
- s2wasm は binaryen でインストールされるコマンド
- アセンブラをS式で表現したもの
- 生成例:
- 拡張子
.wasm
- 生成例
sexpr-wasm -o foo.wasm foo.wast
- sexpr-wasm は sexpr-wasm-prototype でインストールされるコマンド
- S式の
.wast
をバイトコードで表現したもの
- 生成例
ブラウザからwasmを呼び出す方法
現在の方法。
var xhr = new XMLHttpRequest();
xhr.open('GET', 'foo.wasm', true);
xhr.responseType = 'arraybuffer';
xhr.onload = function() {
var binary = xhr.response;
var binarray = new Uint8Array( binary );
var module = Wasm.instantiateModule( binarray, {} ); //
module.exports.foo(); // extern された関数をJSから呼び出せる
};
xhr.send(null);
ネットワークから取得せずとも ArrayBuffer に wasmのソースを突っ込んだオブジェクトを作れればよさそうだが、今のところJSはバイナリを扱う能力が貧弱なので、まあこれでも良い気がする。
無理矢理一般化するならこうだろうか
function loadWasmModule(name) {
return new Promise(done => {
var xhr = new XMLHttpRequest();
xhr.open('GET', name, true);
xhr.responseType = 'arraybuffer';
xhr.onload = function() {
var binary = xhr.response;
var binarray = new Uint8Array( binary );
var module = Wasm.instantiateModule( binarray, {} );
done(module);
};
xhr.send(null);
});
}
loadWasmModule("foo.wasm").then(module => {
module.exports.foo();
});
要は Wasm.instantiateModule( <Uint8array>, {} );
を叩ければJSから呼べる参照が手に入る。
WebAssembly の直近の目標
ここから私見が混じる。
特に明言されたわけではないが、市場の雰囲気から読み取れることを述べる。
なんだかんだ言ってるが、wasmの直近の目標はゲームプラットフォームをブラウザ上に構築することにあると見ている。マーケットとして巨大なゲーム市場をブラウザに持ってこれれば各ベンダの利益に繋がる。オープンプラットフォームを標榜するMozillaが、ともするとエンドユーザーにとっては難読化だと捉えることが可能で本来の思想と反しそうなWASMに熱心なのも、その辺りの経済的な事情があるだろう。(とはいえ、AppStoreでネイティブアプリを売って儲けてるAppleは別だから、WASMやWebGLのようなAPIで歩調が乱れるのが恒例になっているのだが)
現在、モバイル環境でUnityのHTML5吐き出しを実用化するためのボトルネックは2つ存在する。1つはWebGLはGPUを直接叩いてるので高速だが、GPUに渡すために計算する行列計算は結局CPUレベルであること。もう1つはasm.jsが巨大であるため、実行時間以前にパースやデコードでモバイル端末のような貧弱な環境で、数十秒(場合によっては1分以上)固まってしまうこと。
前者を解決するのがSIMD命令をブラウザから直接実行できる仕様 https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/SIMD であり、後者を解決するのがWASM、という流れ。
参考: http://qiita.com/HirokiMiyaoka@github/items/89975a9e8205ced3603f