ご挨拶
この記事は ピクシブ株式会社 Advent Calendar 2017 8日目の記事です。
今年の7月からピクシブの福岡オフィスでアルバイトをしています。@printf_moriken です。
JavaScript が得意でフロントエンドの領域ばかりやっていたのですが、ピクシブでバイトするにあたって Android と iOS アプリの開発もやり始めました。
好きな IEEE は IEEE 754-2008 です。個人的に JavaScript に半精度浮動小数点数を入れようと活動しています1。Stage 1 です。よろしくお願いします。
WebAssembly について
WebAssembly はブラウザでバイナリを実行できるようにするための技術、ファイル形式のことを言います。
旧来の技術に asm.js がありますが、バイナリ化したことによって以下のような利点があります。
- バイナリ化することによるファイル容量(通信量)の削減
- asm.js と比べてコンパイル時のバリデーションのコストが遥かに小さい(速い)
一方で以下のようなデメリットもあります。
- JavaScript にはバイナリを直接埋め込むことが出来ないため、XMLHttpRequest や Fetch API が必要
asm.js は Javacript のコード中に "use asm";
宣言をした特別な函数を埋め込む設計になっている一方で、WebAssembly では一応 Uint8Array でバイナリ配列を埋め込むようなことは出来ますがテキストなので非効率的です23。
WebAssembly の基本的なことについて詳しく知りたい方は @tkihira さんの以下の記事が勉強になります。
WebAssembly の基礎
また今年の Advent Calendar にどしどし投稿されているみたいですので、興味のある方はそちらで最新情報を得てはいかがでしょうか。
WebAssembly Advent Calendar 2017
Emscripten について
Emscripten は C/C++ を LLVM を経由して asm.js に変換し、それを含んだ JavaScript を生成するツールです。
また Binaryen を使うことで asm.js の部分を WebAssembly に変換することが出来ますが、いずれにせよ JavaScript によってラップされる形で出力されます4。
実験的機能に JavaScript にラップされないスタンドアロンビルドがあります5が、まだ安定していないのと、標準ライブラリを自前で用意しないといけない設計でなかなか実用には厳しいです。
WebAssembly のスタンドアロンビルド(脱!Emscripten)
Emscripten では JavaScript でラップされる形となってしまいますが、WebAssembly のバイナリ単体で得たいところです。利点としては以下の通り。
ブラウザにコンパイル後のキャッシュを持たせられる6- WebAssembly と JavaScript の相互のやり取りが容易になる
- Emscripten のラッパーの JavaScript が WebAssembly のパスを持つ……みたいなことをしなくてすむ7
- 気分的に良い✨
WebAssembly をビルドする方法については現在色々なアプローチがあり、 Emscripten Night #5 での @chikoski さんの資料にわかりやすくまとまっています。
https://speakerdeck.com/chikoski/20171018-wasm?slide=9
ここでは以下のツール、言語について軽く触れようと思います。
- AssemblyScript
- dcodeIO/webassembly
- LLVM/Clang
- Rust
AssemblyScript
AssemblyScript は TypeScript のサブセットです。malloc
, free
系も用意してあり、大体のコードは書くことができそうです。
@chikoski さんの記事に使用例が載っています。
AssemblyScriptを使ってTypeScriptのコードを早くしよう
dcodeIO/webassembly
dcodeIO/webassembly は WebAssembly 用に C の標準ライブラリを用意してコンパイルするツールの他、Node.js で WebAssembly が使える環境を提供してくれます。C で書かれた資源を WebAssembly で使えるようにするにはこちらを選ぶと良さそうです8。
@ukyo さんの記事に使用例が載っています。
dcodeIO/webassembly内のツールチェーンでzlibをビルドしてみる
LLVM/Clang
直接 C/C++ を WebAssembly にビルドする方法です。標準ライブラリを自前で用意する必要が有るため、C を使う場合だと前述の dcodeIO/webassembly を使ったほうがよさそうです。
具体的には musl, libcxx のヘッダーファイルを読み込みつつも、実体の函数やクラスは WebAssembly 向けではないため clang には食わせないみたいなことをする必要があります。そして吐き出された WebAssembly を使う時には足りないそれらの函数を JavaScript で補って、WebAssembly の import に食わせるみたいな中々ハッキ―なことをしないといけません9。
Rust
Rust は Mozilla が開発しているプログラミング言語です。もともと Emscripten を経由して WebAssembly にビルドできる言語でしたが、最近直接 LLVM の wasm32-unknown-unknown target にビルドできるようになったみたいです10。
Rust 自体は rust-bindgen を使うことで C/C++ の資源を使うことが出来るみたいですが、あくまでこれは FFI を生成するツールなので、WebAssembly のビルドに活かすのは厳しそうです。
@bokuweb さんの記事に使用例が載っています。
Rust + WebAssembly でpngデコードを行うnode_moduleを作ってみる
個人的な見解
現状だと完全にゼロから WebAssembly を出力したい場合は AssemblyScript を使い、C の資源を使いたい場合は dcodeIO/webassembly を使えばよさそうです。そして腕に自身はある人は Rust を使う。
個人的に vorbis を WebAssembly でデコードしたいのですが、libvorbis が依存している stdio.h 系の一部の函数が dcodeIO/webassembly では使えないみたいなのでなかなか厳しいです……と思っていたら lewton というものを見つけてしまったので、これから Rust を勉強しようかなと思います😂
おわりに
WebAssembly のビルドツールが出揃ってきていよいよ本格的に使えるようになってきましたね! みなさんも Rust を勉強して WebAssembly をプロダクトに投入していきましょう!
本来なら Rust から WebAssembly を生成する例をここに書く予定でしたが、一週間弱で Rust を習得することができませんでした。
明日は SpiderMonkey に pipeline operator を実装したすごいエンジニアの @hakatashi です。引き続き ピクシブ株式会社 Advent Calendar 2017 をお楽しみください。
-
https://qiita.com/printf_moriken/items/1a16d759cefea1b3210e ↩
-
data URL を使ったほうが比較的効率的かな。 ↩
-
ECMAScript Stage 1 で Binary AST が議論されており、もしかしたらそのままバイナリを埋め込める日が来るかもしれません☺ ↩
-
https://github.com/kripken/emscripten/wiki/WebAssembly-Standalone ↩
-
WebAssembly Advent Calendar 2017 3日目の記事 巨大 WebAssembly ファイルのコンパイル時間にも記載されていますが、残念ながら廃止されてしまいました。 ↩IndexedDB
にWebAssembly.Module
を保存できます。 -
Emscripten を使った場合、ビルドした WebAssembly のバイナリのディレクトリを移動するとラッパーの JavaScript から参照できなくなり辛い目に合う……。 ↩