WebAssembly Studio でコンパイルする手順

WebAssembly Studio は Web上で WebAssembly の開発(デバッグコンパイル、実行)が行える IDE です。 ここでは、WebAssembly Studio を使って、C(.c)と TypeScript(.ts)で作成した関数を WebAssembly(.wasm)にコンパイルし、それらをブラウザ上の JavaScript から呼び出す手順について紹介し、最後に WebAssembly を Node.js 上で実行する方法についても紹介します。

注)JavaScript の配列を C/C++ の関数に渡す方法については、こちらを参考にしてください。
注)ブラウザは ChromeFirefox をご利用ください。IE11 で WebAssembly Studio のサイトにアクセスしても何も表示されません???


コンパイルの手順

WebAssembly Studio で main.c 及び main.ts を main.wasm にコンパイルする手順は以下のようになります。

(1)WebAssembly Studio にアクセスします。

(2)Create New Project → Empty C Project → Create → 新しいプロジェクトが生成されます。
 注)TypeScript で作成した関数をコンパイルする場合は、AssemblyScript Project を選択します。

(3)main.c 又は main.ts を開き、プログラムを作成します。(下記参照)
 注)main.html と main.js は使わないので削除しても構いません。

(4)Save → Build → main.wasm が生成されます。

(5)download → wasm-project.zip がダウンロードされます。
 wasm-project.zip を解凍すると、out フォルダの中に main.wasm があるので、下記の main.html と同じフォルダに入れます。

以上です。WebAssembly Studio を使うことで、とても簡単にコンパイルすることができます。

main.c

#define WASM_EXPORT __attribute__((visibility("default")))

WASM_EXPORT double add(double x, double y) { return x + y; }
 

 

main.ts

export function add(x:f64, y:f64):f64 { return x + y; }
 

注)f64 は TypeScript には元々ない型で、AssemblyScript で TypeScript をコンパイルするためにに追加された型です。新たに追加された型はこちらを参照してください。

 

JavaScript から WebAssembly を呼び出す

JavaScript から wasm ファイルを呼び出すには fetch API を使って下記のようにします。

   fetch(wasmファイルのパス)

更に次のようにすれば、C や TypeScript で作成した関数を呼び出すことができます。

   exports.関数名

今回の例では、exports.add が C や TypeScript で作成した関数 add に対応します。

main.html

<!doctype html>
<html>
<body>

<div>
  <input id="Input1" type="text"> + <input id="Input2" type="text">
  <input type="button" value="=" onclick="add('Input1', 'Input2', 'Output');">
  <output id="Output"></output>
</div>

<script src="main.js"></script>

</body>
</html>
 

 

main.js

let exports;
fetch("main.wasm")
.then(response => response.arrayBuffer())
.then(bytes => WebAssembly.compile(bytes))
.then(module => WebAssembly.instantiate(module))
.then(instance => exports = instance.exports)
.catch(console.error);

function add(input1, input2, output) {
  const number1 = Number(document.getElementById(input1).value);
  const number2 = Number(document.getElementById(input2).value);
  document.getElementById(output).innerHTML = exports.add(number1, number2);
}
 

注)exports をグローバル変数として外に出しています。(非同期処理でこういうことをするのは邪道かも知れませんが...)
今回のように catch(console.error) 以降が全て関数定義の場合は問題ないのですが、catch(console.error) 以降のステートメントから直接的、或いは間接的に exports を呼び出そうとすると、当然「exports が未定義です」というエラーが発生するので、上記構文の取扱いには充分注意してください。

注)wasm ファイルはデフォルトではブラウザのキャッシュに保存されますが、保存したくない場合は fetch("main.wasm", {cache:"no-cache"}) のように {cache:"no-cache"} を追記してください。

〈ファイルの配置〉

D:\webassembly\main.html
D:\webassembly\main.js
D:\webassembly\main.wasm

 

Webサーバのインストールと起動

ブラウザ単独では main.wasm を読み込めないので、Node.js を使って簡易のWebサーバを立ち上げます。 まず、Node.js をインストールします。続いて、コマンドプロンプトから下記のように入力すると、Webサーバをインストールして起動させることができます。


npm install http-server -g
cd /d D:\webassembly
http-server

注)html ファイルがあるフォルダ(D:\webassembly)に移動してから、Webサーバを起動させます。
注)「Windows ファイアウォールでブロックされています」の画面が表示されたら、「アクセスを許可する」をクリックしてください。

 

ブラウザからWebサーバにアクセス

Webサーバが起動したら、ブラウザのアドレスバーに下記のように入力してください。


http://localhost:8080/main.html

注)ブラウザは ChromeFirefox をご利用ください。IE11 は対応していません。

すると、次のような画面が表示されるので、

+

数値を入力し、=ボタンをクリックして、結果が表示されたら成功です^^

 

WebAssembly を Node.js 上で実行

WebAssembly(main.wasm)を Node.js 上で実行するには、下記の main.js のようにします。ちょっとしたテストをする場合は、Webサーバを起動する必要がないのでこちらの方がお手軽です。


cd /d D:\webassembly
node main.js

 

main.js

const instance = new WebAssembly.Instance(new WebAssembly.Module(require("fs").readFileSync("main.wasm")));
console.log(instance.exports.add(1, 2));
 

 

〈ファイルの配置〉

D:\webassembly\main.js
D:\webassembly\main.wasm

 

 

結論

Gaussの消去法で連立方程式を解くプログラムを使って、C のネイティブコードと C をコンパイルした WebAssembly の速度を比較したところ、WebAssembly(Node.jsで実行)の方が約2.2倍遅いという結果でした。C のネイティブコードに比べ約2.2倍遅いと言っても、Swift のネイティブコード(Swift for Windowsでテスト)より約1.6倍速いので、C から生成した WebAssembly は想像以上に速いというのが実感です。但し、wasm ファイルのサイズは1MBを超えていました^^

「WebAssembly は使える!」というのが私の結論です。

WebAssembly の利用が本格化すれば、今までネイティブコードでやっていたような数値計算やデータ処理、ゲームと言った負荷のかかる処理もブラウザでやれるので、これから更にWebアプリの可能性が広がりそうですね。
また、Windows の場合は WebAssembly 経由で DLL を呼び出せば、C/C++ のライブラリも活用できそうです。

追記:
あらゆるデバイスやコンピュータ、オペレーティングシステムで WebAssembly を実行することを目的とした WASI(WebAssembly System Interface)や、WebAssembly のランタイムの一つである Wasmtime も今後注目ですね。

 


参考サイト