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自体はよほどの理由がない限り一時変数を使うのをお勧めします)