【Ruby】benchmark-ipsを利用したベンチマークの実行・確認方法

Ruby

前回、Ruby標準のBenchmarkモジュールを利用したベンチマークの実行・確認方法でRuby標準のBenchmarkモジュールを利用したベンチマークテストの実行方法について紹介しました。

今回は、Benchmarkモジュールによる計測を『繰り返し回数/時間』という表現に拡張したbenchmark-ipsを利用したベンチマークの実行・確認方法について紹介します。

benchmark-ipsを利用したベンチマークテストの実行方法

benchmark-ipsを利用したベンチマークテストの実行方法について紹介します。

gemのインストール

Gemfile

group :development do
  gem 'benchmark-ips'
end
$ bundle

ベンチマークテストの処理を埋め込む

ベンチマークテストの実行方法はBenchmarkモジュールを利用した場合と似ています。
計測対象の処理をBenchmark.ipsで囲むことで、ベンチマークテストの結果がコンソールログに出力されます。

ただし、benchmark-ipsは設定時間の間ベンチマークテストを繰り返し実行してくれるため、Benchmarkモジュールのように試行回数をわざわざ明記する必要がありません。

benchmark-ipsを利用したベンチマークテストの文法は以下の通りです。

Benchmark.ips do |x|
  x.report { 計測する処理 }
end

ラベル付けをする場合は以下の通りです。

Benchmark.ips do |x|
  x.report(ラベル名) { 計測するロジック }
end

benchmark-ipsで設定できるwarmup(ウォームアップ時間。デフォルトは2)とtime(実行時間。デフォルトは5)を変更したい場合は以下のようにします。

Benchmark.ips do |x|

  # 方法1
  x.config(time: 5, warmup: 2)

  # 方法2
  x.time = 5
  x.warmup = 2

  #
  # ベンチマークの実行
  #
end

x.compare!でベンチマークの比較結果が出力されます。x.compare!の記述は任意です。compare!を利用する場合はラベル付けが必須です。

Benchmark.ips do |x|
  x.report(ラベル名1) { 計測するロジック }
  x.report(ラベル名2) { 計測するロジック }

  x.compare! # ラベル名1とラベル名2の比較結果が出力される
end

ベンチマークの確認方法

benchmark-ipsは『繰り返し回数/時間(i/ms)』で処理を評価します。
つまり、数値が大きいほど高速ということを意味しています。

たとえばRailsアプリケーション内でpluck(:id)map(&:id)のベンチマークテストを実行する場合は以下のようになります。


# 計測中にSQLのログが出力されないようにする old_logger = ActiveRecord::Base.logger ActiveRecord::Base.logger = nil # ベンチマークテストの実行 Benchmark.ips do |x| x.report("pluck(:id)") { Student.all.pluck(:id) } x.report("map(&:id)") { Student.all.map(&:id) } x.compare! end # ログの設定を元に戻す ActiveRecord::Base.logger = old_logger

結果は以下の通りです。

実行結果

Warming up --------------------------------------
          pluck(:id)     1.359k i/100ms
           map(&:id)   908.000  i/100ms
Calculating -------------------------------------
          pluck(:id)     13.704k (± 9.3%) i/s -     67. 5.009733s
           map(&:id)      8.929k (± 5.7%) i/s -     44. 4.999683s
Comparison:
          pluck(:id):    13703.9 i/s
           map(&:id):     8929.1 i/s - 1.53x  (± 0.00) slower

Calculatingを確認すると、pluck(:id)のほうがmap(&:id)よりも数値が大きいです。つまりpluck(:id)のほうが高速ということを意味しています。

pluck(:id)のほうが高速であることは、Comparisonmap(&:id)に対してslowerという記述があることからも分かります。

参考: Benchmarkモジュールとbenchmark-ipsのベンチマークテスト比較

比較のため同じベンチマークテストをBenchmarkモジュールで行った場合とbenchmark-ipsで行った場合を掲載しておきます。

Benchmarkモジュールの場合

n = 20000
Benchmark.bm(10) do |x|
  x.report("pluck(:id)") { n.times { Student.all.pluck(:id) } }
  x.report("map(&:id)") { n.times { Student.all.map(&:id) } }
end

実行結果

                 user     system      total        real
pluck(:id)   1.444674   0.019573   1.464247 (  1.468776)
map(&:id)    2.077519   0.005431   2.082950 (  2.089609)

benchmark-ipsの場合

Benchmark.ips do |x|
  x.report("pluck(:id)") { Student.all.pluck(:id) }
  x.report("map(&:id)") { Student.all.map(&:id) }
end

実行結果

Warming up --------------------------------------
          pluck(:id)     1.471k i/100ms
           map(&:id)   912.000  i/100ms
Calculating -------------------------------------
          pluck(:id)     12.889k (± 5.4%) i/s -     64.724k in   5.037596s
           map(&:id)      8.921k (± 5.1%) i/s -     44.688k in   5.023643s

まとめ

benchmark-ipsの利用方法
  • 『Benchmark.ips do~end』で処理を囲むことでベンチマークテストができる
  • ベンチマークは『繰り返し回数/時間』で評価される。値が大きいほど高速
  • benchmark-ipsはBenchモジュールと違い、繰り返し回数を明記する必要はない

参考