目次
- WebAssemblyとは
- サンプルを動かしてみる
- サンプルの動作を解説する
- まとめ
1. WebAssemblyとは
WebAssembly(wasm)とは、JavaScript高速化の最新技術です。
JavaScriptの高速化技術であるasm.jsがさらに進化したものです。
C/C++, Rust, Goなどの言語で記述したソースコードを、LLVMなどを用いて.wasm拡張子のバイナリにコンパイルし、JSから呼び出すことが可能になります。
1.1. WebAssembly誕生の経緯
事前にコンパイルを行い、できる処理に制限を加える(DOMやUIの操作が制限され、
値計算がメインになっている)ことで高速化を可能にしたのがasm.jsでした。
しかし、asm.jsのソースコードをブラウザ側でコンパイルするため、ソースコードの容量が大きいとロードタイムが長くなることが問題でした。
それを解決するため、ソースコードをバイナリにして容量を節約しようという発想です。
1.2. WebAssemblyの歴史
Wikipediaによると、2017/03/07に初のWebAssembly対応ブラウザFirefox 52.0がリリースされました。
2017/05/14現在では、Google Chrome 57~58, Firefox 52~53, Opera 44, Chrome for Android 57 が対応しています。Safari, Edge は未対応です。
現在のブラウザ対応状況は下記のCan I useで確認できます。
https://caniuse.com/#search=wasm
現状では対応ブラウザも不十分ですし、資料やドキュメントも不足していますが、私はWebAssemblyを勉強する価値があると思います。
1.3. WebAssemblyのいいところまとめ
2. サンプルを動かしてみる
WebAssemblyについていくつか説明しましたが、やはり理解するのに一番いいのは使ってみることでしょう。
というわけで、WebAssemblyのDeveloper's Guideに書いてあるGetting Startedを実際にやってみましょう。
※ここからはDockerとnpmがインストールされている前提で話を進めますので、入っていない方は事前に準備してください。
※追記:githubにソースを上げました。
2.1. WebAssembly の Getting Started
Developer’s Guide - WebAssembly
こちらのDeveloper's Guideでは環境構築の方法とサンプルコードの動作確認の方法が記述されています。
環境構築としてはemsdk(emscripten sdk)のインストールが推奨されています。
これをまじめにやると2時間ほどかかり容量も15GBほどとられますので、この記事ではDockerを使って環境構築をスキップします。
2.2. サンプルコードをコンパイルする
適当な新規ディレクトリに下記のコードをhello.cとして保存します。
#include <stdio.h> int main(int argc, char ** argv) { printf("Hello, world!¥n"); return 0; }
Dockerコンテナ上でコンパイルを実行する。
docker run --rm -t -v $(pwd):/src gifnksm/emscripten-incoming emcc hello.c -s WASM=1 -o hello.html
- docker run でDockerコンテナを立ち上げます
- --rmオプションでDockerイメージのキャッシュを破棄します
- -v $(pwd):/srcでカレントディレクトリをDockerコンテナの/srcにマウントします
- gifnksm/emscripten-incomingはDockerイメージの名前でemsdkがインストールされています。
- emcc hello.c -s WASM=1 -o hello.htmlはコンパイルのコマンドで、WebAssembly - Developer's Guideに書いてあるものと全く同じものです。
2.3. サンプルの動作を確認する。
http-server コマンドでサーバを立ち上げ、ブラウザからhttp://localhost:8080/hello.htmlにアクセスして動作確認を行います。
次のような画面になれば成功です。
動かない場合は下記の注意事項を確認してください。
- Edge, SafariはWebAssemblyに非対応なので、対応ブラウザで確認してください
- http-serverコマンドが登録されていない場合はnpm install --global http-serverで導入してください
- http-serverは8080番ポートが空いてない場合は違うポート番号をとります。Connection refused系のエラーが出た場合はhttp-serverコマンドを実行したターミナルにポート番号が書いてあると思いますので確認してください
2.4. 成果物の確認
下記のファイルが生成されていることを確認します。
- hello.wasm
- hello.js
- hello.html
hello.wasm
hello.cをLLVMなどを用いてwasm形式にコンパイルしたバイナリです。
hello.js
hello.wasmのコンパイルなどをラップしたjsファイルで、emsdkのemccコマンドが自動生成してくれます。
hello.html
先ほどブラウザで確認したHTMLです。hello.jsを読み込んで画面に反映させています。
3. サンプルの動作を解説する
emccコマンドで生成したjsを使ってwasmを読み込んだ場合、読み込みが完了した際に.cのmain関数が実行されます。
今回実行したemccのオプションではwasm側でprintf()を実行すると、JS側のModule.print()が実行されるようになっているようです。
Module.print()の定義はhello.htmlの最後の方にあります。
<script> var Module = { ... print: (function() {
var element = document.getElementById('output');
if (element) element.value = ''; // clear browser cache
return function(text) {
if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
console.log(text);
if (element) {
element.value += text + "\n";
element.scrollTop = element.scrollHeight; // focus on bottom
}
}; })(), ... }; </script> <script async type="text/javascript" src="hello.js"></script>
このようにsample.jsを読み込む前にModuleオブジェクトを定義することで、wasm読み込みに干渉することができます。
4. まとめ
この記事ではWebAssemblyとは何かを説明し、最小構成のサンプルを実行するとこまで書きました。
せっかく高速化技術なので、今後はJSとの速度比較サンプルなどを作ってみたいと思います。