Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

asm.js x emscripten: The foundation of the next level Web games

1,627 views

Published on

Game requires high performance to its run times. asm.js brings Web browsers high performance JavaScript processing and allows us to build heavy games on the top of Web browsers. In this talk, we can see 1 )the background of asm.js 2) asm.js details and 3) overview of emscripten, a compiler which emits asm.js formatted JavaScript code from C/C++.

asm.js の詳細と emscripten の簡単な使い方について解説します。

Published in: Technology

asm.js x emscripten: The foundation of the next level Web games

  1. 1. asm.jsxEmscripten: Thefoundationofthenextlevelwebgames MozillaJapanテクニカルマーケティング 清水智公(nshimizu@mozilla-japan.org/@chikoski) 第63回HTML5とか勉強会withhtml5jゲーム部発足記念合同勉強会
  2. 2. N.Shimizu • Mozilla Japan (L10N, Game, Devtools, Web Animation) • html5j Web プラットフォーム部、
 Firefox OS、Firefox OS コードリーディング、
 html5j ゲーム部 (NEW!) • @chikoski • プログラミング言語、分類、ベイジアン、サッカー、圏論(NEW!)
  3. 3. 文字列 実行コード
  4. 4. 文字列 実行コード抽象構文木トークン列 中間表現 字句解析 構文解析 意味解析 最適化・コード生成
  5. 5. 文字列 実行コード抽象構文木トークン列 中間表現 字句解析 構文解析 意味解析 最適化・コード生成
  6. 6. 文字列 AST バイトコード 字句解析 / 構文解析 生成 実行 * AST: Abstract Syntax Tree / 抽象構文木
  7. 7. function addOne(a){ return a + 1; }
  8. 8. loc op ----- -- main: 00000: getarg 0 00003: one 00004: add 00005: return 00006: retrval
  9. 9. aのデータ型 a + 1 のデータ型 number number undefined number null number string string object string
  10. 10. Just In Time Compile
  11. 11. データ型 Data Type
  12. 12. 文字列 字句解析 / 構文解析 実行 バイトコードAST Baseline Compiled Code MIR Iron Compiled Code 実行と プロファイル 実行 Bail 生成 Baseline Compile Iron Build Iron Compile * AST: Abstract Syntax Tree / 抽象構文木  MIR: Medium-level Intermediate Representation / 中間表現
  13. 13. * AST: Abstract Syntax Tree / 抽象構文木  MIR: Medium-level Intermediate Representation / 中間表現 文字列 字句解析 / 構文解析 実行 バイトコードAST Baseline Compiled Code MIR Iron Compiled Code 実行と プロファイル 実行 Bail 生成 Baseline Compile Iron Build Iron Compile • JITは重い • hot code(よく実行されるコード)しかコンパイルされない • JITが始まるまでは、遅いまま • 型推論に失敗するなどして、JITをやり直すことがある ダウンロード 実行開始 コンパイル
  14. 14. Ahead-Of-Time Compile
  15. 15. AOT ダウンロード コンパイル 実行開始 JIT ダウンロード 実行開始 コンパイル Native ダウンロードコンパイル 実行開始
  16. 16. データ型 Data Type
  17. 17. asm.js
  18. 18. asm.js:anextraordinarilyoptimizable,low-levelsubsetofJavaScript • JavaScript として評価可能 • 数値計算、関数の定義と呼び出し、ArrayBufferの操作のみ可能 • その他のことは不可能 • 型アノテーションと、フォーマルな検証プロセス • FFIを通じたJavaScript との相互呼び出し • ArrayBuffer の共有によるデータの受け渡し
  19. 19. function add1(a){ return a + 1; }
  20. 20. function Peano(stdlib, ffi, heap){ "use asm"; function add1(a){ a = a | 0; return (a + 1) | 0; } return { suc: add1 } }
  21. 21. const add1 = Peano(window).suc; const zero = 0; const one = add1(zero); console.log(one); // 1 asm.js で定義したモジュールの利用
  22. 22. function Peano(stdlib, ffi, heap){ "use asm"; // 外部からインポートするシンボルの宣言 // 関数宣言 // 関数表の宣言 // モジュールのエキスポート } asm モジュールの構造
  23. 23. #include <stdio.h> // 標準ライブラリのインクルード #include "log.h"   // その他のライブラリのインクルード extern void log(int); // 外部からインポートするシンボルの宣言 int add1(int n){ // 関数宣言 log(n); return n + 1; }
  24. 24. function Peano(stdlib, ffi, heap){ "use asm"; var abs = stdlib.Math.abs; // 標準ライブラリからのインポート var z = ffi.zero | 0; // 定数の受け渡し var log = ffi.log; // JS関数のインポート 外部からインポートするシンボルの宣言
  25. 25. const ffi = { zero: 0, log: text => console.log(text) }; const add1 = Peano(window, ffi).suc; JS から asm.js へのシンボルの受け渡し
  26. 26. function Peano(stdlib, ffi, heap){ "use asm"; var abs = stdlib.Math.abs; // 標準ライブラリからのインポート var z = ffi.zero | 0; // 定数の受け渡し var log = ffi.log; // JS関数のインポート 型アノテーション
  27. 27. 変数x のアノテーション 引数 x の型 x = x | 0; int x = +x; double x = f(x); float
  28. 28. function addOne(n){ // 引数の型宣言 // 変数宣言 // 関数本体の記述 } 関数宣言の構造
  29. 29. function addOne(n){ n = n | 0; // 引数の型宣言 var one = 1; // 変数宣言 one = (one + n) | 0; // 関数本体 return one | 0; }
  30. 30. 返り値の ret アノテーション 返り値の型 return +ret; double return ret | 0; int return 3; double return f(ret); float return; void
  31. 31. function addOne(n){ n = n | 0; // 引数の型宣言 var one = 1; // 変数宣言 one = (one + n) | 0; // 関数本体 return one | 0; } 関数の型: (引数の型) → 返り値の型
  32. 32. function addOne(n){ n = n | 0; return (n + 1)| 0; } function addTwo(n){ n = n | 0; return (n + 2) | 0; } int -> int の関数
  33. 33. var addFunctions = [addOne, addTwo]; 関数表宣言(関数ポインタの列)
  34. 34. function Peano(stdlib, ffi, heap){ "use asm"; var abs = stdlib.Math.abs; // 標準ライブラリからのインポート var z = ffi.zero | 0; // 定数の受け渡し var log = ffi.log; // JS関数のインポート 標準ライブラリ、外部関数、ヒープ
  35. 35. 標準ライブラリ 型 Infinity NaN double Math.acos Math.asin Math.atan Math.cos Math.sin Math.tan Math.exp (double?) → double
  36. 36. 標準ライブラリ 型 Math.ceil Math.floor Math.sqrt (double?) → double ∧ (float?) → float Math.abs (signed) → signed ∧ (double?) → double ∧ (float?) → float Math.min Math.max (int, int…) → signed ∧ (double, double…) → double Math.atan2 Math.pow (double?, double?) → double
  37. 37. 標準ライブラリ 型 Math.imul (int, int) → signed Math.fround fround Math.E Math.LN10 Math.LN2 Math.LOG2E Math.LOG10E Math.PI Math.SQRT1_2 Math.SQRT2 double
  38. 38. var log = ffi.log; // 外部関数 log(n | 0);     // これはリンク時にエラー log((n | 0) >>> 0); // これはリンクできる 外部関数の呼び出しにも配慮が必要
  39. 39. asm.js における型(矢印は継承を表す)
  40. 40. JavaScript の環境と行き来できる型
  41. 41. 単項演算子 型 + (signed) → double ∧
 (unsigned) → double ∧ (double?) → double ∧
 (float?) → double - (int) → intish ∧
 (double?) → double ∧ (float?) → floatish ~ (intish) → signed ! (int) → int
  42. 42. 二項演算子 型 + (double, double) → double ∧ (float?, float?) → floatish - (double?, double?) → double ∧ (float?, float?) → floatish * (double?, double?) → double ∧ (float?, float?) → floatish / (signed, signed) → intish ∧ (unsigned, unsigned) → intish ∧ (double?, double?) → double ∧ (float?, float?) → floatish
  43. 43. 二項演算子 型 % (signed, signed) → intish ∧ (unsigned, unsigned) → intish ∧ (double?, double?) → double |, &, ^, <<, >> (intish, intish) → signed >>> (intish, intish) → unsigned <, <=, >, >=, ==, != (signed, signed) → int ∧ (unsigned, unsigned) → int ∧ (double, double) → int ∧ (float, float) → int
  44. 44. var memo = new stdlib.Uint32Array(heap); if(memo[n >> 2] | 0 != 0){ return memo[n >> 2] | 0; } if(n >>> 0 > 2){ result = ((fib(n - 1 | 0) | 0) + (fib(n - 2 | 0) | 0)) | 0; } return result | 0; heap の利用例
  45. 45. Heap View Type 要素サイズ load type store type Uint8Array 1 intish intish Int8Array 1 intish intish Uint16Array 2 intish intish Int16Array 2 intish intish Uint32Array 4 intish intish Int32Array 4 intish intish Float32Array 4 float? floatish, double? Float64Array 8 double? float?, double?
  46. 46. var memo = new stdlib.Uint32Array(heap); if(memo[n >> 2] | 0 != 0){ return memo[n >> 2] | 0; } if(n >>> 0 > 2){ result = ((fib(n - 1 | 0) | 0) + (fib(n - 2 | 0) | 0)) | 0; } return result | 0;log2(要素のバイト数) ぶんだけ右にシフトしなくてはならない
  47. 47. • 例外を投げることなく return まで到達すること • 全ての属性アクセスは、データアクセスとして解決されること • heap があるなら、それは ArrayBuffer であること • heap の大きさは [212 , 224 ) 、もしくは 224 の倍数であること • stdlibから取られたシンボルは、
 標準ライブラリ中のものを指すこと リンクに成功する条件
  48. 48. % emcc -o hello.js hello.c % node hello.js hello world % emcc -o hello.html hello.c % open hello.html -o オプションで html / js へ出力
  49. 49. EmscriptencompilesC/C++intoJavaScriptinasm.jsformat • ファイル入出力: XHR に書き換え / ファイルを JS に結合 • JavaScript からC/C++コードの呼び出し • cwrap / ccall を利用 • WebIDL を作成し、binding を生成 • CからJavaScriptコードの呼び出し • EM_ASMマクロの利用したコード埋め込み • 外部関数 (extern)として呼び出し、リンク時に解決
  50. 50. ファイルアクセス
  51. 51. % emcc —preload-file hello.txt -o file.html fileio.c —preload-file オプションをつけると read を XHR に変更
  52. 52. % emcc —preload-file hello.txt -o file.html fileio.c % emcc --embed-file hello.txt -o hello.html file.c —embed-file オプションで、ファイルを埋め込む
  53. 53. ccall / cwrap
  54. 54. extern "C" { unsigned int fib(unsigned int n){ if(n < 3){ return 1; } return fib(n - 1) + fib(n - 2); } } C++ での記述: エキスポートする関数名のマングリングを防ぐ
  55. 55. % emcc -o fib.html -s EXPORTED_FUNCTIONS="['_fib']" fib.cpp 不要なコードの削除を防ぐために EXPORTED_FUNCTIONS を指定
  56. 56. var fib = Module.cwrap("fib", "number", ["number"]); console.log(fib(30)); var result = Module.ccall("fib", "number", ["number"], [30]); console.log(result); JavaScriptからの呼び出し: 識別子、型情報の指定が必要
  57. 57. WebIDL
  58. 58. class Peano{ public: Peano(); int current(); void suc(); }; C++ でクラスを定義
  59. 59. class Peano{ public: Peano(); int current(); void suc(); }; interface Peano{ void Peano(); long current(); void suc(); }; インタフェースを定義するWebIDLファイルを作成
  60. 60. % python tools/webidl_binder.py peano.webidl peano-glue グルーコードを生成
  61. 61. #include "peano.cpp" #include "peano-glue.cpp" ラッパーを作成
  62. 62. % emcc -o peano.html --post-js peano-glue.js peano.cpp peano-wrapper.cpp —post-js オプションをつけてemcc コンパイル
  63. 63. p = new Module.Peano(); console.log(p.current()); // 0 を出力 p.next(); console.log(p.current()); // 1 を出力 JavaScriptからはModule オブジェクトの属性として参照できる
  64. 64. CへのJavaScriptの埋め込み
  65. 65. #include <stdio.h> #include <emscripten.h> int main(int argc, char **argv){ EM_ASM({ alert("hello!"); }); } EM_ASM マクロを使って JavaScript コードを埋め込める
  66. 66. x = EM_ASM_INT({ console.log("argument:"+ [$0, $1]); return Math.pow($0, $1); }, 2, 24); printf("x = %dn", x); 引数、返り値のあるコードの埋め込み例
  67. 67. CからJavaScriptコードの呼び出し
  68. 68. #include <stdio.h> extern "C"{ extern int id(int); extern void hi(); } 外部関数を宣言
  69. 69. int main(int argc, char **argv){ int x = 10; printf("id(%d) = %dn", x, id(x)); hi(); return 0; } リンクされる前提で呼び出す
  70. 70. mergeInto(LibraryManager.library, { id: function(n){ return n; }, hi: function(){ alert("hi"); } }); library.js に関数を実装
  71. 71. -rw-r--r-- 1 chiko staff 81B 2 17 19:25 hello.c -rw-r--r-- 1 chiko staff 100K 3 24 18:17 hello.html -rw-r--r-- 1 chiko staff 466K 3 24 18:17 hello.js

×