Bench.as

2008.7.12

普段使っている簡単なベンチマーク用のクラスを晒してみます。この手のやつってみんな自作のものを持っているか、もっといいものがあると思いますが、こういうやり方もあるということで・・。

早速使い方ですが、計測したい部分を以下のようにbeginとendで囲みます。

var ary:Array = [];

Bench.begin("loop");
for (var i:uint = 0; i < 500; ++i) {
  var sum:uint = 0;

  Bench.begin("subloop");
  for (var j:uint = 0; j < i; ++j) {
    Bench.begin("add");
    sum += j;
    Bench.end();
  }
  Bench.end();

  ary.push(sum);
}
Bench.end();

/*
                 time       rate      count
loop              830      1.000          1
subloop           826      0.995        500
add               223      0.269     124750
*/

そうすると、コメントにあるような表がトレースされます。表の1列目はbeginの引数に与えたラベル、2列目がミリ秒単位の処理時間、3列目が処理時間の割合、4列目が呼び出された回数になります。beginの引数を省略すればその部分の結果は出力しないようにもできます。また、ネストした場合、トップレベルのベンチマークが終わったときにまとめて出力されるので、他のtrace文が入っていても結果が混ざって見にくくなる心配はありません。

これにはもう一つ使い方があって、timesというメソッドにコールバック関数と回数を指定して計ることもできます。endやtimesは計測した時間を返すので、必要であれば以下のようにしてどの程度パフォーマンスが改善されたかを求めることができます。

function func() {
  var a = 1, b = "", c = {};
  a + b + c;
}

function typedFunc():void {
  var a:Number = 1, b:String = "", c:Object = {};
  a + b + c;
}

var count:uint = 100000;
Bench.times(null, function():void {
  var t0:uint = Bench.times("before", func, count);
  var t1:uint = Bench.times("after", typedFunc, count);
  trace(Math.round(100 * (t0 / t1 - 1)) + "%の高速化");
});

/*
39%の高速化
                 time       rate      count
before            759      0.582          1
after             545      0.418          1
*/

もし、ラベル名が長くて表が崩れてしまうときは、列の横幅を大きめに設定しておくこともできます。

Bench.columnWidth = 20;

ソースですが、ブログに貼るとなんか記号が化けるのでこちらから。

追記:
いくつか気になる点があったので、本文中の例とソースを以下のような修正版に差し替えました。

  • ループ等で同じラベルを持つ計測が複数回行われた場合、表示を1行にまとめるようにした。
  • 出力する表にカラム名を加えた。
  • 出力する表に呼び出し回数の列を加えた。
  • 無名関数を指定した後にラベルが続くと見にくいので、timesの引数の順序を変えた。

さらに追記:
出力する表のソートキーを選べるようにしました。Bench.orderというプロパティに定数を代入する感じになります。

Bench.order = Bench.BEGIN; //実行が開始された順で表示 (デフォルト)
Bench.order = Bench.LABEL; //ラベルの辞書順で表示
Bench.order = Bench.TIME; //時間がかかったものから表示
Bench.order = Bench.COUNT; //呼び出し回数が多いものから表示