読者です 読者をやめる 読者になる 読者になる

【WebAssembly】Getting Started をDocker で試す

目次

  1. WebAssemblyとは
  2. サンプルを動かしてみる
  3. サンプルの動作を解説する
  4. まとめ

 

f:id:shogonir:20170514025514p:plain

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のいいところまとめ

  1. 高速化
  2. 型がある世界でプログラムを書ける
  3. 最終的にバイナリになるので、アルゴリズムソースコードをユーザから隠せる

 

2. サンプルを動かしてみる

WebAssemblyについていくつか説明しましたが、やはり理解するのに一番いいのは使ってみることでしょう。
というわけで、WebAssemblyのDeveloper's Guideに書いてあるGetting Startedを実際にやってみましょう。

※ここからはDockerとnpmがインストールされている前提で話を進めますので、入っていない方は事前に準備してください。

※追記:githubにソースを上げました。

github.com

 

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にアクセスして動作確認を行います。
次のような画面になれば成功です。

f:id:shogonir:20170514023351p:plain

動かない場合は下記の注意事項を確認してください。

  • 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との速度比較サンプルなどを作ってみたいと思います。