[AS3] 偶数を2で割る [Edit]

偶数を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)。

コメント

この記事にコメントを書く

記事に対するテクニカルな質問はご遠慮ください(利用規約)。

その他の記事