2018年10月における WebAssembly の状況を記録するために、現時点での WebAssembly を試してみることにしました。
目次
1. WebAssembly とは?
これまで、ウェブサイト上で何かプログラミングによる処理を行いたい場合(サーバー側ではなくブラウザ側の話しです)は、JavaScript を使うしかありませんでした。JavaScript は唯一のウェブ標準プログラミング言語だったのです。これに続いて、新たなウェブの標準として登場したプログラミング言語が「WebAssembly」です。
JavaScript よりも実行速度が求められる処理で使用することを主な目的として、主要ウェブブラウザベンダが、W3C WebAssembly Working Group というグループを介して開発しています。
WebAssembly は低水準なプログラミング言語ですので、人間が直接記述するものではありません。C/C++/Rust といったプログラミング言語で記述したコードを WebAssembly に変換して使用します。モジュール化できるので便利ですし、JavaScript と連携することも可能です。
WebAssembly を実行する方法
WebAssembly を実行するには 2通りの方法があります。
1つ目は、JavaScript API を使って wasm ファイル(WebAssembly のバイナリファイル)を読み込む方法です。既に主要ブラウザでは WebAssembly
という JavaScript のオブジェクトが実装されているのですが、このオブジェクトを通して WebAssembly JavaScript API を使うことができます。この API を使うと wasm ファイルを読み込んで利用することができます。
もう1つは、<script type='module'>
といったタグを書くことで、直接 wasm ファイルをブラウザに読み込ませて処理させる方法です。なのですが、主要ブラウザにおいてもまだこの機能は実装されていません。
2. WebAssembly を使ってみる
Developer’s Guide – WebAssembly に、簡単な使用例が載っていますので、これを試してみることにしました。
環境
- Windows 10
- WSL (Ubuntu 16.04 TLS)
作業
WSL のターミナルからの操作が中心となります。
1. Windows 10 installation
Windows 10 の環境なので、まずは WSL (Windows Subsystem for Linux) を利用して Linux をインストールします。しかし、今回の環境では既に Ubuntu がインストール済みでした。
その次に進み、python 2.7 を導入します。
$ sudo apt update
$ sudo apt upgrade
$ sudo apt install python2.7 python-pip
2. Downloading the Toolchain
C/C++ のコードを WebAssembly にコンパイルするツールを導入します。具体的には、Emscripten というツールになります。
まず、今回作業するディレクトリ ~/local
に移動しておきます(どこでもよいです)。
$ cd ~/local
以下のコマンドで、Emscripten をインストールします。
$ git clone https://github.com/juj/emsdk.git
$ cd emsdk
$ ./emsdk install latest
$ ./emsdk activate latest
この操作、何がどこにインストールされるのか分からないので不気味でしたが、su (またはsudo) で実行するわけではないので、ひどいことにはならないだろうと判断して進めました。
いざやってみると、どうやらカレントディレクトリ (emsdk) 直下にインストールが行われたようです。そのため、アンインストールするには、このディレクトリを削除すればよさそうです(削除に関しては、README.txt に書いてありました)。
上記の最後のコマンド「./emsdk activate latest
」を実際に実行した結果が以下です。
$ ./emsdk activate latest
Writing .emscripten configuration file to user home directory /mnt/c/Users/foo/
The Emscripten configuration file /mnt/c/Users/foo/.emscripten has been rewritten with the following contents:
import os
LLVM_ROOT='/mnt/c/Users/foo/local/emsdk/clang/e1.38.13_64bit'
EMSCRIPTEN_NATIVE_OPTIMIZER='/mnt/c/Users/foo/local/emsdk/clang/e1.38.13_64bit/optimizer'
BINARYEN_ROOT='/mnt/c/Users/foo/local/emsdk/clang/e1.38.13_64bit/binaryen'
NODE_JS='/mnt/c/Users/foo/local/emsdk/node/8.9.1_64bit/bin/node'
EMSCRIPTEN_ROOT='/mnt/c/Users/foo/local/emsdk/emscripten/1.38.13'
SPIDERMONKEY_ENGINE = ''
V8_ENGINE = ''
TEMP_DIR = '/tmp'
COMPILER_ENGINE = NODE_JS
JS_ENGINES = [NODE_JS]
To conveniently access the selected set of tools from the command line, consider adding the following directories to PATH, or call 'source ./emsdk_env.sh' to do this for you.
/mnt/c/Users/foo/local/emsdk:/mnt/c/Users/foo/local/emsdk/clang/e1.38.13_64bit:/mnt/c/Users/foo/local/emsdk/node/8.9.1_64bit/bin:/mnt/c/Users/foo/local/emsdk/emscripten/1.38.13
Set the following tools as active:
clang-e1.38.13-64bit
node-8.9.1-64bit
emscripten-1.38.13
※ ユーザー名は「foo」としてあります。
Emscripten を使うと、以下の変換処理を行うことができます。
C/C++ ファイル
↓ 変換処理
wasmファイル, HTMLファイル, JavaScriptファイル
HTMLファイルと JavaScriptファイルまで出力されることに少し違和感があるかもしれませんが、この2つのファイルには wasm ファイルを実行するために必要な記述が含まれています。
3. Environment setup after installation (環境変数のセット)
「./emsdk activate latest
」の実行結果にも書いてありましたが、このツールを使うには環境変数を追加する必要があります。これは、用意されているシェルスクリプトを実行することで可能です。
$ source ./emsdk_env.sh --build=Release
実際に実行した結果を以下に示します。
$ source ./emsdk_env.sh --build=Release
Adding directories to PATH:
PATH += /mnt/c/Users/foo/local/emsdk
PATH += /mnt/c/Users/foo/local/emsdk/clang/e1.38.13_64bit
PATH += /mnt/c/Users/foo/local/emsdk/node/8.9.1_64bit/bin
PATH += /mnt/c/Users/foo/local/emsdk/emscripten/1.38.13
Setting environment variables:
EMSDK = /mnt/c/Users/foo/local/emsdk
EM_CONFIG = /mnt/c/Users/foo/.emscripten
LLVM_ROOT = /mnt/c/Users/foo/local/emsdk/clang/e1.38.13_64bit
EMSCRIPTEN_NATIVE_OPTIMIZER = /mnt/c/Users/foo/local/emsdk/clang/e1.38.13_64bit/optimizer
BINARYEN_ROOT = /mnt/c/Users/foo/local/emsdk/clang/e1.38.13_64bit/binaryen
EMSDK_NODE = /mnt/c/Users/foo/local/emsdk/node/8.9.1_64bit/bin/node
EMSCRIPTEN = /mnt/c/Users/foo/local/emsdk/emscripten/1.38.13
4. Compile and run a simple program
では、ここからは実際に C言語で簡単なプログラムを書き、Emscripten を使ってコンパイルします。
この作業のためのディレクトリを用意して、そこに移動しておきます。
$ mkdir ~/tmp/hello
$ cd ~/tmp/hello
以下の内容を記述した hello.c
ファイルを作成します。
#include <stdio.h>
int main(int argc, char ** argv) {
printf("Hello, world!\n");
}
コンパイルします。
$ emcc hello.c -s WASM=1 -o hello.html
オプションの簡単な説明
-s WASM=1
: wasm ファイルを生成します。-o hello.html
: HTMLファイルも生成します(拡張子 .html がポイントです)。
結果、以下のファイルが生成されました。
- hello.html
- hello.js
- hello.wasm
あとは、生成されたウェブページをウェブブラウザで表示するだけなのですが、これにはウェブサーバーを立てる必要があります。ウェブサーバーを用意せずに、生成された HTMLファイルをウェブブラウザにドラッグ・アンド・ドロップして表示しても、wasm の読み込み処理は実行されません。なぜかというと、生成された JavaScript の処理の中に、fetch という API で wasmファイルを読み取る部分があるのですが、fetch はローカルのファイル(file scheme)読み込みに対応していないからです。そのため、ウェブサーバー経由でアクセスして、http scheme (もしくは https scheme) で fetch させないといけないのです。
Emscripten についてくる emrun
というコマンドを使うと、簡易的なウェブサーバーを立てることができますので、これを利用します。
ウェブサーバーの起動
$ emrun --no_browser --port 8080 .
補足
emrun
でなくても、ウェブサーバーを立てられるなら他のツールでもよいです。
例えば、php
コマンドが使えるのであれば、以下でも代用できます。
$ php -S localhost:8080 -t .
ウェブブラウザで、以下のURLにアクセスします。
無事、「Hello, world!」と表示されました。WebAssembly を実行することができたようです。
3. おわりに
今回生成された hello.html
や hello.js
ファイルの中を見ると、wasm ファイルを利用するためのコードが想像以上のボリュームで記述されています。特に hello.js
は 2,000行以上記述されていました。WebAssembly を利用するために、これほどの量のコードを記述しなければいけないというのは、あまり現実的でないような気がします。
将来的には <script type='module'>
といった HTMLタグを書くことで、直接 wasm ファイルをブラウザに読み込ませて実行できる(?)そうですが、速くこれを実現して欲しいなと思いました。
>今回生成された hello.html や hello.js ファイルの中を見ると、wasm ファイルを利用する
>ためのコードが想像以上のボリュームで記述されています。
>特に hello.js は 2,000行以上記述されていました。WebAssembly を利用するために、
>これほどの量のコードを記述しなければいけないというのは、あまり現実的でないような気がします。
自慢になってしまいそうで言いにくのですが、
http://nowsmartsoft.atwebpages.com
http://nowsmartsoft.atwebpages.com/demo1/index.html
http://nowsmartsoft.atwebpages.com/demo2/index.html
においてあるWasmプログラムは、「ページのソースを表示」などで
index.html を見たり、スタートアップ用の start.js をブラウザの URL 欄に
http://nowsmartsoft.atwebpages.com/demo1/start.js
入れて見てみると分かりますが、以下のようにだいぶコンパクトになっています:
demo1/index.html : 67行
demo1/start.js : 303行
demo1/test.wasm : 89.9 KB
Emscripten (のemcc) は使わず、自作の C++ nex コンパイラ nwsc と、
clangで作りました。
コンパイル時間も emcc よりだいぶ速いです。特に最後のリンク時間が恐らく
数10倍~100倍くらい速いと思います。