かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

Ruby benchmark-ips で処理のベンチマークを取る

👆で Ruby の case 文でクラス名を判定する時に、クラスそのものでもclass.name でも判定できるので、どちらのほうが処理が速いのか気になりました。
JS脳的なイメィジだとなんとなくStringでの完全一致の方が速そうな気がしたのでど、どうなんでしょう?と先輩に訊いてみた所、benchmark-ips を使ったベンチマークのとり方を教えてくれました。

benchmark-ips

このGemでベンチマークすると「処理回数(イテレーション回数)/秒」を計測してくれます。 次の項で説明するRuby標準のbenchmarkライブラリは自分で実行回数を指定しなければならないのですが、こちらはその手間がなく、かつ処理ごとに何倍の速度差があるかを出してくれるので便利です。
ref. Rubyでベンチマークを取る - Qiita

install

$ gem install benchmark-ips

使い方
公式から引用 GitHub - evanphx/benchmark-ips: Provides iteration per second benchmarking for Ruby

require 'benchmark/ips'

Benchmark.ips do |x|
  # Configure the number of seconds used during
  # the warmup phase (default 2) and calculation phase (default 5)
  x.config(:time => 5, :warmup => 2)

  # These parameters can also be configured this way
  x.time = 5
  x.warmup = 2

  # Typical mode, runs the block as many times as it can
  x.report("addition") { 1 + 2 }

  # To reduce overhead, the number of iterations is passed in
  # and the block must run the code the specific number of times.
  # Used for when the workload is very small and any overhead
  # introduces incorrectable errors.
  x.report("addition2") do |times|
    i = 0
    while i < times
      1 + 2
      i += 1
    end
  end

  # To reduce overhead even more, grafts the code given into
  # the loop that performs the iterations internally to reduce
  # overhead. Typically not needed, use the |times| form instead.
  x.report("addition3", "1 + 2")

  # Really long labels should be formatted correctly
  x.report("addition-test-long-label") { 1 + 2 }

  # Compare the iterations per second of the various reports!
  x.compare!
end

Benchmark.ips 内で report に表示するテスト名と測定したい処理を渡して、最後に compare! を書いておけばどれが一番早かったといったレポートが表示できるようです。

case でクラスを判定する処理速度を測ってみる

クラスそのもので比較した場合と、class.name で文字列にしたものとで比較してみました。

class_name_case_bentchmark.rb

require 'benchmark/ips'

class A
  def compare_as_class
    100000.times do
      case self
      when B
      when A
      end
    end
  end

  def compare_as_string
    100000.times do
      case self.class.name
      when "B"
      when "A"
      end
    end
  end

  def compare_as_variable_string
    ckassName = self.class.name
    100000.times do
      case ckassName
      when "B"
      when "A"
      end
    end
  end
end

class B
end

Benchmark.ips do |x|
  x.config(:time => 5, :warmup => 2)

  x.report("self") { A.new.compare_as_class }
  x.report("self.class.name") { A.new.compare_as_string }
  x.report("string") { A.new.compare_as_variable_string }

  x.compare!
end

👇 測定

$ ruby class_name_case_bentchmark.rb
Warming up --------------------------------------
                self    11.000  i/100ms
     self.class.name     7.000  i/100ms
              string    13.000  i/100ms
Calculating -------------------------------------
                self    114.599  (± 2.6%) i/s -    583.000  in   5.091943s
     self.class.name     74.064  (± 5.4%) i/s -    371.000  in   5.024619s
              string    130.946  (± 2.3%) i/s -    663.000  in   5.065531s

Comparison:
              string:      130.9 i/s
                self:      114.6 i/s - 1.14x  slower
     self.class.name:       74.1 i/s - 1.77x  slower

Ruby v2.5.5 で100000回試行してみた結果としては、クラス名を変数にしておき文字列で比較した方が若干高速なようでした。
ただ class.name にアクセスするのに時間がかかるようなので、何千回と連続で判別させるのではないのであれば、クラスそのもので比較するで十分だと思いました。


[参考]