memorandums

日々のメモです。

Benchmarkの合計を求める

Rubyにコードの範囲を指定して処理時間を計測する便利クラス「Benchmark」があります(今日知りました)。使い方は以下のような感じです。

require 'benchmark'

puts Benchmark.measure {
  for i in 1..1000
    puts 'hello'
  end
}
puts Benchmark::CAPTION

実行結果は以下の通りです。

0.000000 0.000000 0.000000 ( 0.005730)
user system total real

これはいい!と思ったのですが、何度も呼ばれる処理の場合、計測結果が何度も表示されてしまいます。合計が知りたいのに。。。そこでこのありがたいBenchmarkクラスの処理結果をいじって合計を求めるクラスを作りました。処理時間は上記の通り、短すぎると0になってしまいますのでrealtimeを使うことにします。

■benchmark_sum.rb

class Benchmark_sum
  @@total_time = {}
  @@total_times = {}
  def self.add(s)
    begin
      @@total_time[current_method] += s.to_f
      @@total_times[current_method] += 1
    rescue NoMethodError
      @@total_time[current_method] = 0.0 
      @@total_times[current_method] = 0 
      retry
    end 
  end 
  def self.result
    @@total_time.each {|k,v| puts k+": " + v.to_s + ' ' + @@total_times[k].to_s}
  end 
end

class Object
  def current_method
    caller[1].scan(/`(.*)'/).to_s
  end 
end

使い方は以下のような感じです。

require 'benchmark'
require 'benchmark_sum'

def a
  data = []
  Benchmark_sum.add( Benchmark.realtime {
  for i in 1..100
    data << rand(1000)
  end 
  bubblesort(data)
})
end
 
#http://d.hatena.ne.jp/yasutomo57jp/20080819/1219141752 より拝借させていただきました。
def bubblesort(array)
  Benchmark_sum.add( Benchmark.realtime {
  for i in 0...array.length-1
    for j in 0...array.length-1-i
      array[j+1],array[j]=array[j],array[j+1] if array[j] > array[j+1]
    end 
  end 
})
  return array
end 

100.times {a} 

Benchmark_sum.result

実行結果は以下の通りです。見方は最初がメソッド名、次が処理時間の合計、最後が呼び出し回数です。

a: 0.929216146469116 100
bubblesort: 0.921257495880127 100

ちなみにBenchmark_sumはメソッドの処理時間を計測することを前提としています。同一メソッド内にBenchmarkを複数入れるにはメソッド名を自動取得にせず手動で都度与えるように変更すればいいと思います。