S-JIS[2011-02-14/2011-02-20] 変更履歴

Scala Benchmark

ScalaBenchmarkは、ベンチマークをとる(実行速度を表示する)為のトレイト


概要

Benchmarkトレイトを使って実行時間を計測するには、以下のようにする。

  1. scala.testing.Benchmarkトレイトをミックスインしたクラスを作る。
  2. runメソッドをオーバーライドして、時間を計りたい処理を記述する。
  3. runBenchmarkを呼び出す。(計測回数を指定する)(個人的には、startというメソッド名が良かったなぁ。Threadでそういう風に意識に染み付いてるw)

Benchmarkはmainメソッドも持っているので、Benchmarkを継承したobjectを作れば、scalaコマンドでベンチマークを実行することも出来る。

使ってみた感じでは、計測の初回は実行時間が多めに出ることが多い気がする。(ベンチマークではよくある事)
計測回数は5回くらいでは少ないかも。


REPLでの実行例

REPLで実行する例。

new scala.testing.Benchmark {
  def run() = {
    List.range(1, 100*10000).sum
  }
}.runBenchmark(5)
res0: List[Long] = List(328, 359, 344, 360, 343)
runBenchmarkの引数で指定された回数だけ実行し、
実行時間のリストが返ってくる。単位はミリ秒。

なお、1回計測する度にGC(Platform.collectGarbage(System.gc()))が実行されるようになっている。
new scala.testing.Benchmark {
  def run() = {
    Array.range(1, 100*10000).sum
  }
}.runBenchmark(5)
res1: List[Long] = List(188, 156, 141, 140, 140)
new scala.testing.Benchmark {
  def run() = {
    List.range(1, 10000).sum
  }
}.runBenchmark(5)
res2: List[Long] = List(0, 0, 0, 0, 0)
1回の処理が早すぎる場合、multiplierを設定することで、
1回の計測時間内にmultiplier回の処理を実行するように出来る。

(左記下側の例では、コンストラクター内でmultiplierに100をセットしている)
new scala.testing.Benchmark {
  multiplier = 100

  def run() = {
    List.range(1, 10000).sum
  }
}.runBenchmark(5)
res3: List[Long] = List(203, 203, 187, 187, 203)

REPL上で何度も実行するなら、こんな関数を作っちゃうのも便利かも。

def benchmark(times: Int, mult: Int = 1)(f: =>Any) = new scala.testing.Benchmark{ multiplier = mult; def run() = f }.runBenchmark(times)
scala> benchmark(5){ List.range(1, 100*10000).sum }
res4: List[Long] = List(407, 391, 422, 344, 328)

scala> benchmark(5,100){ List.range(1, 10000).sum }
res5: List[Long] = List(188, 187, 187, 172, 188)

ついでに、最大値・最小値を除いた平均も表示する関数を作ってみた。[2011-02-20]
(3回以上測定する前提)

implicit def benchmark2report(list:List[Long]) = new { def report() = { println(list); println((list.sum - list.max - list.min).toDouble / (list.size-2)) }}
scala benchmark(10){ List.range(1, 100*10000).sum }.report
List(594, 421, 422, 438, 422, 422, 437, 437, 437, 438)
431.625

mainからの実行例

Benchmarkを継承したオブジェクトを作り、scalaコマンドで実行する例。
(Benchmarkトレイトはmainメソッドを持っているので、実行することが出来る)

import scala.testing.Benchmark

object Sample extends Benchmark {
  def run() = {
    List.range(1, 10000).sum
  }
}
>scala Sample
Usage: scala benchmarks.program <runs>
   or: scala benchmarks.program <runs> <multiplier>

    The benchmark is run <runs> times, forcing a garbage collection between runs. The optional
    <multiplier> causes the benchmark to be repeated <multiplier> times, each time for <runs>
    executions.

第1引数で計測回数(runBenchmark()の引数)、
第2引数でmultiplier(1回の計測当たりの実行回数)
を指定する。

>scala Sample 5
Sample$         31              0               0               0               0

>scala Sample 5 100
Sample$         250             203             203             203             203

prefixをオーバーライドしておくと、最初に表示されるメッセージ(デフォルトではクラス名)が変えられる。

import scala.testing.Benchmark

object Sample extends Benchmark {
  override def prefix = "List.sum"

  def run() = {
    List.range(1, 10000).sum
  }
}
>scala Sample 5 100
List.sum                250             203             203             203             203

Scala目次へ戻る / 技術メモへ戻る
メールの送信先:ひしだま