偶数を2で割るとき[*1]は、ビット単位の右シフト演算子>>を使うと処理が速いといわれます[*2]。実際にFlash CS4 Professionalの[ムービープレビュー]でテストしてみると、環境やスクリプトの記述により、結果にばらつきが出てくるようです。
[*1] もう少し正確には、割る数が2の累乗で商が整数になる場合です。
[*2] たとえば、polygonal labs「Bitwise gems - fast integer math」参照。
まずは、以下のフレームアクションを試します。筆者の環境[*3]では、MacintoshとWindowsでともに、ビット単位の右シフト演算子>>を使った方が10%前後速くなります。
スクリプト001■ビット単位の右シフト演算を割り算と比べるvar nCount:uint = 100000000;
var i:uint;
var nAnswer:Number;
var nStart:uint;
// division
nStart = getTimer();
for (i=0; i<nCount; i+=2) {
nAnswer=i/2;
}
var t0:uint=getTimer()-nStart;
trace("division: "+t0);
// bitwise right shift
nStart=getTimer();
for (i=0; i<nCount; i+=2) {
nAnswer=i>>1;
}
var t1:uint=getTimer()-nStart;
trace("bitwise right shift: "+t1);
// result
trace("speed up(%): "+int((1-t1/t0)*100));
上記スクリプト001の計測結果は、[ムービープレビュー]するたびに、多少幅が出るようです。そこで、Timerクラスを使って、同じ処理を何度か繰返してみることにします(スクリプト002)。
スクリプト002■Timerクラスで処理を繰返すvar myTimer:Timer = new Timer(10,10);
myTimer.addEventListener(TimerEvent.TIMER, test);
myTimer.start();
function test(eventObject:TimerEvent):void {
var nCount:uint = 100000000;
var i:uint;
var nAnswer:Number;
var nStart:uint;
// division
nStart = getTimer();
for (i=0; i<nCount; i+=2) {
nAnswer=i/2;
}
var t0:uint=getTimer()-nStart;
trace("division: "+t0);
// bitwise right shift
nStart=getTimer();
for (i=0; i<nCount; i+=2) {
nAnswer=i>>1;
}
var t1:uint=getTimer()-nStart;
trace("bitwise right shift: "+t1);
// result
trace("speed up(%): "+int((1-t1/t0)*100));
}
上記スクリプト002を試してみると、筆者の環境ではまずどちらの演算も、スクリプト001より処理時間が少し長くかかるようになりました。そして、Macintoshでは速度の差が広がります。ところが、Windows環境ではビット単位の右シフト演算より、割り算の方がわずかに速いという結果になりました。
そこで、前掲スクリプト002に修正を加えます。Timerインスタンスのイベントリスナーとして関数test()を直接登録するのでなく、別のリスナー関数からtest()を呼出してみます(スクリプト003)。すると、Macintoshではさほど変わりないものの、Windowsで演算速度が逆転するという現象は解消します(ただし、速度差はほとんどありません)。
スクリプト003■リスナー関数から改めてテスト用関数を呼出すvar myTimer:Timer = new Timer(10,10);
myTimer.addEventListener(TimerEvent.TIMER, _test);
myTimer.start();
function _test(eventObject:TimerEvent):void {
test();
}
function test():void {
var nCount:uint = 100000000;
var i:uint;
var nAnswer:Number;
var nStart:uint;
// division
nStart = getTimer();
for (i=0; i<nCount; i+=2) {
nAnswer=i/2;
}
var t0:uint=getTimer()-nStart;
trace("division: "+t0);
// bitwise right shift
nStart=getTimer();
for (i=0; i<nCount; i+=2) {
nAnswer=i>>1;
}
var t1:uint=getTimer()-nStart;
trace("bitwise right shift: "+t1);
// result
trace("speed up(%): "+int((1-t1/t0)*100));
}
リスナー関数にはイベントオブジェクトが渡されますので、呼出し時にその受渡しの負荷が若干加わることは理解できます。しかし、上記のテスト用スクリプトでは、純粋に演算のループ処理だけを計測しています。したがって、その処理になぜ変化が生じるのかは、今のところ定かではありません。
[*3] Flash CS4 Professional/Flash Player 10.0.2.54/Mac OS X.4.11およびWindows Vista Home Premium (Service Pack 1)。