ゼロから始めるWebAssembly

  • 148
    いいね
  • 1
    コメント

学習記録

WebAssemblyとは

ブラウザ上でクライアントサイドのスクリプトとして効率的に動くバイナリフォーマットです。
現在JavaScriptより軽量で高速な処理を提供するためにベンダー各社で開発されています。
現時点ではDOM, WebAPIへのアクセスとかできません。GCもない。なので現状使いどころは重い処理をWebAssembly部分に投げ出すイメージです。
スレッドとかもFuture Workらしい。
https://github.com/WebAssembly/design/blob/master/FutureFeatures.md#threads

もちろんWebを意識しているが、最終的にはWeb以外でも使えるような思想です。一つのバイナリを作ってしまえばどんなところでも動くようなReact Nativeのようなところを目指しています。
https://github.com/WebAssembly/design/blob/master/NonWeb.md

現状JavaScriptの置き換えには程遠いですが、だんだんWebAssemblyだけでできる範囲が広がるはずです。

まぁここら辺はQiitaにもたくさんいい記事あるので適当に探してください。

http://qiita.com/kenju/items/3ec95aebd30bc95b862c
http://qiita.com/mizchi/items/d7af1625654be4b3e798

何が美味しいのか

まずはJavaScriptの立ち上がりから終わりまでをおさらいします
通常JavaScriptのライフサイクルはこんな感じになります。

Screen Shot 2017-03-05 at 3.41.29.png

これらの動作は個別のまとまりで実行されるわけではなく、少しのパースが行われ、次にいくつかの実行が行われ、次にコンパイルされ、さらにパースされ、さらに実行されるというような動作が起きます。

ChromeのDeveloper tool でみると

Screen Shot 2017-03-03 at 0.16.51.png

コンパイルに時間がかかっていることがわかります。
これでも初期のJavaScriptから見ると改善されてて、最近のブラウザはJITコンパイラおよび多様な最適化とトリックを使用して、可能な限り高速の実行を実現しようとします。このおかげでフロントが重いアプリケーションも成り立っています。

WebAssemblyではどうなるか

Fetch

実行前にファイルを取得する部分です。WebAssemblyではJavaScriptよりファイルサイズが小さくなることが期待されています。
このため純粋にネットワークにかかる時間が短縮されます

パース

JavaScriptの場合だと一旦ASTに変換されます。ブラウザは最初に必要な部分だけ処理して残りはスタブを作るなどして処理の実行遅らせます。
さらにASTからJSエンジンが読み込めるようにByteCode(中間表現)を生成します。

これらの部分に結構時間を費やします。PCだと結構早くてもモバイルでは時間がかる。
以下は1MBのJavaScriptを実行した際の表です。


参考 https://medium.com/dev-channel/javascript-start-up-performance-69200f43b201#.8ohbun4zc

一方でWebAssemblyにはこの工程は必要ありません。すでに中間表現化されており実行エンジンが読み込める形にしてあるからです。
デコードとValidateをするのみです。

コンパイルと最適化

JavaScriptは実行時コンパイラ(JIT)を使用することで実行する間にコンパイルされます。
WebAssemblyでは実行前にコンパイルされます。
最適化についてはコンパイルを開始する前に、使用されている型を確認するためにコードを実行する時間を費やす必要が無くなります。
またコンパラ(LLVM)では、より多くの最適化が事前に行われています。 そのためコンパイルと最適化に必要な作業が少なくて済みます。

再最適化

JavaScriptのJITでは型情報の統計を取るためにある程度コードを動かします。ある程度の型情報が集まったところでより最適化するコンパイルを行います。
しかしコンパイルされた状態は常に維持されずに時には、コンパイルした状態を捨てて再度コンパイルをすることもあります。
また再度コンパイルされるまでは関数が低速で動くしかありません。

WebAssemblyでは型情報は明確です。ということでJITが型情報の統計をとって再最適化を行う必要が無くなります。

実行

JavaScriptのJITによる最適化ではJITの構造を知ってないできない最適化が存在しますが、正直僕はJITの構造とか何にも意識せずプログラミングしています。
どちらかと言えば読みやすさを優先しています。詳しい人でもそこまで速度を意識したプログラミングができるかと言えばどうなんでしょうか。。。
またJSのエンジン毎にJITの実装も違うのでより良い最適化は難しいということが言えます。

WebAssemblyだとその手の最適化をプログラマが行う必要がありません。コンパイラが最適化を行ってくれるので、誰が書いてもある程度の速さを提供できます。

Garbage collection

https://github.com/WebAssembly/design/blob/master/GC.md#native-gc
JSのGCとは別にNative GCという機能を追加する(予定)らしいです。現在はありません。
GCの不都合な実行などで悩まされることもあるので、自分でメモリを確保、解放したい人たちにはメモリを自分で管理できるのは嬉しいことだと思います。

環境構築

ググってるといろんな情報があって環境構築でかなり手間取ったんですけど、基本的には以下のページの通りに構築すれば動くようなものが出来上がります。
http://webassembly.org/getting-started/developers-guide/

ビルドまでできたらあとはChrome Canary 版で動きます
https://www.google.com/chrome/browser/canary.html

Demo

こちら
https://1984weed.github.io/webassembly-sample/src/nqueen/

GitHub
https://github.com/1984weed/webassembly-sample

動かしてみた

上記のDemoでn-queenを計算させてみました。
n = 14とかで計算させると
JS => 375ms
WebAssembly => 284ms

実行回数も少ないので厳密な速さとは言えないですが、実行速度の速さは確認することができました。
今回はファイルサイズがどちらも少ないのでFetchやパースの時間の計測ができませんでしたが、ここら辺も確かめたいです。

まとめ

WebAssemblyはIOTやMobileまたは重たいJavaScriptのコードを要するようなサイト、とにかく高速に実行したいWebアプリケーション(シューティングゲームとか)にとってすごく嬉しい技術だと言えます。

まだまだつまずくところが多く例えば今回はmathの関数が使えずにpowを自作で関数を用意するなどを行いました。(ここら辺はよくわかってないです)
期待している技術なので動向を追っていきたいです。

参考
https://hacks.mozilla.org/2017/02/what-makes-webassembly-fast/
https://medium.com/dev-channel/javascript-start-up-performance-69200f43b201#.5g6np0kx4
https://html5experts.jp/chikoski/18964/