ビット演算による小数の整数化
http://d.hatena.ne.jp/babydaemons/20110629/JavaScript_without_Math_floor_function を読んでなんとなくやりたくなったのでやります。
ご存知の通り、Javascriptでもビット演算はできます。
一般にビット演算は通常の四則演算よりも高速だと思われますので使えるところでは積極的に使っていきたいところであります。
よく知られている整数化の手法としては以下のようなものがあります。
// 論理積 var int = float&-1; // 論理和 var int = float|0; // 排他的論理和 var int = float^0; // 2回の補数算出 var int = ~~float; // 0回のビットシフト var int = float<<0; // Math.floor() var int = Math.floor(float);
でもどの程度高速なのか、というのが分からないので簡単にベンチマークを取ってみました。
ベンチマークを行う上で使用したコードは以下の通り。
function calc(n) { var f = 3.141592; var tmp = 0; var start = new Date(); for (var i=0; i<10000000; i++) { tmp = f&-1; // ここを少しずつ書き換えて判定しています。 } var end = new Date(); return end-start; } for (var i=0, ret=[], sum=0; i<100; i++) { ret[i] = calc(i); console.log(i+"回目: "+ret[i]+"ms"); sum += ret[i]; } console.log("100回の平均: "+sum/100+"ms");
これを Windows 7上のIE9, Firefox10, Chrome17 で実行しました。
実行環境マシンの大まかなスペックは以下の通り。
公平を期すため、すべてのブラウザにおいて about:blank を表示した上でコンソールから実行しました。
結果は以下のようになりました。
// IE9 論理積 : 9.88ms 論理和 : 9.9ms 排他的論理和 : 9.85ms 補数算出 : 113.24ms ビットシフト : 9.92ms Math.floor() : 2791ms // Firefox10 論理積 : 37.94ms 論理和 : 37.13ms 排他的論理和 : 37.91ms 補数算出 : 38.25ms ビットシフト : 38.19ms Math.floor() : 5114.93ms // Chrome17 論理積 : 10.05ms 論理和 : 10.06ms 排他的論理和 : 9.8ms 補数算出 : 9.98ms ビットシフト : 9.9ms Math.floor() : 9.95ms
なんとビット演算を直接叩いた時、IE9がChrome並に速いのでした。
ただし、IE9に関して言えば、100回の計算中、最初の2回はコンパイルされないらしくスコアがガクッと落ちています。*1
ですので上記のセットをもう一度行い、その結果を記入しています。
また、IE9ではクソ時間の掛かっていたMath.floor()
による計算がChromeではほとんど他の演算法と変わらないところを見ると、内部でビット演算に置き換えているような気がいたします。
V8エンジンの中身を見ているわけではないので何とも言えませんが…。
あと、全体的にFirefoxが遅いです。
6週間ごとにメジャーバージョンアップしていますが、なにが高速化しているのかさっぱり分かりません。
1000万回演算しても1/100秒程度*2の世界なので、ぶっちゃけそこまで気にする必要ないですね。ということでした。
比較対象のMath.floor()
が(Chrome以外では)あまりにも遅いので、使えるなら積極的にビット演算使っていった方がよさそうです…。