Javascriptの数値演算とasm.js

いちごパック > asm.jsの解説 > Javascriptの数値演算とasm.js

Javascriptについて

JavascriptはWebブラウザなどで使われるスクリプト言語です。 ECMAScriptとして標準化されています。
スクリプト言語で書かれたスクリプトをそのまま実行すると実行速度が遅くなり、 重い演算処理をするスクリプトではその遅さが体感でわかることもあります。 しかし、ほとんどのWebブラウザはJast-In-Time(JIT)コンパイラと呼ばれる高速化技術を搭載しているため、 Javascriptはスクリプト言語としては比較的高速に実行できることが知られています。

Javascriptの数値演算

Javascriptでは、主に倍精度浮動小数点演算を数値演算に利用します。 例として、1から100までの和を求める次の疑似コードを考えてみます。
ichigoval = 0;
for (ichigoloop = 1; ichigoloop <= 100;
     ichigoloop = ichigoloop + 1) {
  ichigoval = ichigoval + ichigoloop;
}
C言語やC++言語であれば、ichigovalやichigoloopとしてintなどの整数型を使うでしょう。 しかしJavascriptでは型が用意されておらず、 変数(var)としてichigovalやichigoloopを宣言すると、 これらはC/C++言語のdoubleに相当する倍精度浮動小数点演算型として演算されます。 つまり、C/C++言語で次のコードを書くことに相当します。
double ichigoloop;
double ichigoval = 0;
for (ichigoloop = 1; ichigoloop <= 100;
     ichigoloop = ichigoloop + 1) {
  ichigoval = ichigoval + ichigoloop;
}
倍精度浮動小数点演算型を使ったコードを高速化しても、 整数型を使うC/C++言語と比べると数段遅くなってしまうでしょう。

asm.js

asm.jsは、Javascriptの文法を保ったまま整数演算(int系)や単精度浮動小数点演算(float)を書けるようにして、 この文法に対応したスクリプト実行環境でJavascriptの数値演算を速くするための技術です。 asm.jsに対応した関数を用意し、その関数がasm.js対応であると宣言することで、 高速化の恩恵を受けられるようになります。
asm.jsはFirefox用につくられた技術ですが、Chrome、Edgeも対応しているようです。 Safariはasm.jsだけを特別扱いせず、スクリプト中にある同様の演算を検出し、それらに対応した高速化を行うようです。 したがってasm.jsに対応したJavascriptを準備すれば、 多くのWebブラウザでJavascriptを高速に実行できる可能性があります。
asm.jsは既存のJavascriptと100%互換性を保つように設計された、 機能制限版のJavascriptです。 asm.jsは主にスクリプトを機械的に生成するためにつくられたようで、 互換性のために特殊な文法を使います。 したがって、asm.jsで書かれたJavascriptとしては読みにくいスクリプトになります。
例として、1を足して返すだけの関数を考えてみましょう。
function addone(ichigo1) {
  return ichigo1 + 1;
}
この単純な関数であっても、 次の2つの理由により、Javascriptとしては整数型(int)を採用することができません。
  • ichigo1の型は32ビットの整数型(int)で表現できない、一般の倍精度浮動小数点型(double)かもしれません。
  • ichigo1が32ビットの整数型(int)であっても、2147483647かもしれません。
  • 2147483647に1を足すと、倍精度浮動小数点型では2147483648ですが、 整数型ではオーバーフローして、-2147483648になってしまいます。 そこでasm.jsでは、 整数型を採用しても結果が変わらないことを保証するための小細工を追加します。
    はじめに、引数に|0をつけて自分自身に代入させることで、 引数を強制的に32ビットの整数型(int)として解釈させます。
      ichigo1 = (ichigo1|0);
    
    32ビットの整数型(int)に何か整数値を加算する場合は、 結果に|0をつけて、強制的に32ビットの整数型(int)として解釈させます。
      return (ichigo1 + 1)|0;
    
    これらの修正を適用すると、 asm.jsに対応し、1を足して返すだけの関数は次のように書けます。
    function addone(ichigo1) {
      ichigo1 = (ichigo1|0);
      return (ichigo1 + 1)|0;
    }
    
    実際にasm.jsでaddone()を使うには、addone()に加えて、 この関数がasm.jsに対応した特別な関数であることを宣言するスクリプトも書く必要があります。
    asm.jsのために決められた方法でスクリプトを書くことで、数値演算の結果を変えずに、 この文法に対応したスクリプト実行環境でのJavascriptの数値演算を速くできるようになります。