nazolabo

なぞさんのブログ

AS3処理の疑問

Math.abs()を使わず絶対値を求める


Absolute value


//version 1
i = x < 0 ? -x : x;

//version 2
i = (x ^ (x >> 31)) - (x >> 31);


この単純なコードでなんと2,500%高速化。さらにビット演算を組み合わせるとさらに加えて20%高速化。

http://actionscript.g.hatena.ne.jp/ConquestArrow/20070621/1182359767

これがちょっと気になったので調べてみた。

パターン

  • ケース1(単純にMath.abs)
b = Math.abs(a);
  • ケース2(三項演算子)
b = a < 0 ? -a : a;
  • ケース3(if)
if (a < 0) {
  b = -a;
} else {
  b = a;
}
  • ケース4(ケース2を外部関数化)
private function abs(a:int):int {
    return a < 0 ? -a : a;
}

b = this.abs(a)
  • ケース5(if+乗算)
if (a < 0) {
  b = a * -1;
} else {
  b = a;
}
  • ケース6(bit演算)
b = (a ^ (a >> 31)) - (a >> 31);

結果

上記のをそれぞれ10000000回回した結果(a=-3)(PentiumM 1.1GHz)

ケース1(単純にMath.abs) 2912ms
ケース2(三項演算子 342ms
ケース3(if) 343ms
ケース4(ケース2を外部関数化) 2216ms
ケース5(if+乗算) 316ms
ケース6(bit演算) 113ms

結論

  • 関数呼び出しはものすごく遅いので、速度が必要な場面ではインライン展開すべき(inline関数とかあれば楽だけど…)
  • bit演算は可読性が極端に落ちるので、劇的に速度向上が期待できる場面以外では使わないほうがいい
  • 三項演算子とifは大差ない(おそらく内部的には同じだと思われる)ので、可読性と好みで判断する

こういう高速化は、「何故そういう記述で高速化できるのか」がわかってないと、適切に利用できないので、正しく検証して利用したほうがいいと思います。逆に言えば、それがわかっていると、他の場面でも応用が利くと思います。
あとbit演算に感動した人は、ゲームプログラミング関係で調べるといろいろ出てきます。たとえばxor swapネタならこのあたりとか。(ちなみに加減算swapもオーバーフローする値でおかしくなる可能性があるので、swap自体はよほどの理由がない限り一時変数を使うのをお勧めします)