4
1

More than 1 year has passed since last update.

Ruby の benchmark ライブラリと愉快な仲間たち

Last updated at Posted at 2022-12-28

推測するな、計測せよ。

Load, Aim, Shoot
Load, Aim, Measure
(ベンチマークライブラリを読み込み、計測対象を定め、計測する。)

この記事は?

Ruby でベンチマークを計測するための機能や Gem をご紹介します。

バージョン情報

$ ruby -v
ruby 3.1.3p185 (2022-11-24 revision 1a6b16756e) [arm64-darwin21]

$ gem list | grep benchmark
benchmark (default: 0.2.0)
benchmark-ips (2.10.0)
benchmark-memory (0.2.0)

benchmark ライブラリ

Benchmark モジュール

Benchmark.bm

require 'benchmark'

# 16 はラベルの長さ。
# 'String#end_with?'.size #=> 16
Benchmark.bm(16) do |x|
  x.report('String#match?') { '🐶🐱🐰'.match?(/🐰\z/) }
  x.report('String#end_with?') { '🐶🐱🐰'.end_with?('🐰') }
end
                       user     system      total        real
String#match?      0.000007   0.000000   0.000007 (  0.000003)
String#end_with?   0.000002   0.000000   0.000002 (  0.000002)
  • user: ユーザ CPU 時間
    • Ruby プログラム自体の実行に費やされた時間
  • system: システム CPU 時間
    • Ruby プログラムが発行したシステムコールなどで OS のコードが実行された時間
  • total: user + system
  • real: 経過時間

1 回の実行だと処理時間のブレが大きくて参考にならないので、試行回数を増やす。

require 'benchmark'

n = 100_000

Benchmark.bm(16) do |x|
  x.report('String#match?') { n.times { '🐶🐱🐰'.match?(/🐰\z/) } }
  x.report('String#end_with?') { n.times { '🐶🐱🐰'.end_with?('🐰') } }
end
                       user     system      total        real
String#match?      0.016460   0.000097   0.016557 (  0.016555)
String#end_with?   0.012503   0.000139   0.012642 (  0.012654)

適切な実行回数を決めたりループしたりするのがめんどくさい 😇

Benchmark.bmbm

ベンチマークの結果は GC の影響によって歪められてしまうことがあります。このメソッドは与えられたブロックを二度実行する事によってこの影響を最小化します。一回目は実行環境を安定化するためにリハーサルとして実行します。二回目は本番として実行します。

require 'benchmark'

n = 100_000

Benchmark.bmbm(16) do |x|
  x.report('String#match?') { n.times { '🐶🐱🐰'.match?(/🐰\z/) } }
  x.report('String#end_with?') { n.times { '🐶🐱🐰'.end_with?('🐰') } }
end
Rehearsal ----------------------------------------------------
String#match?      0.018132   0.000103   0.018235 (  0.018232)
String#end_with?   0.011770   0.000088   0.011858 (  0.011859)
------------------------------------------- total: 0.030093sec

                       user     system      total        real
String#match?      0.010967   0.000062   0.011029 (  0.011041)
String#end_with?   0.008412   0.000071   0.008483 (  0.008485)

Gem

benchmark-ips

Benchmark.ips

benchmark-ips benchmarks a blocks iterations/second. For short snippits of code, ips automatically figures out how many times to run the code to get interesting data. No more guessing at random iteration counts!

  • 1 秒あたりの実行回数を計測する。
  • ベンチマーク時の処理の実行回数を自動で決定してくれる 💖
require 'benchmark/ips'

Benchmark.ips do |x|
  x.report('String#match?') { '🐶🐱🐰'.match?(/🐰\z/) }
  x.report('String#end_with?') { '🐶🐱🐰'.end_with?('🐰') }
  
  x.compare!
end
Warming up --------------------------------------
       String#match?   923.101k i/100ms
    String#end_with?     1.052M i/100ms
Calculating -------------------------------------
       String#match?      9.242M (± 0.4%) i/s -     47.078M in   5.094220s
    String#end_with?     10.539M (± 0.4%) i/s -     53.658M in   5.091593s

Comparison:
    String#end_with?: 10538712.3 i/s
       String#match?:  9241654.3 i/s - 1.14x  (± 0.00) slower

benchmark-memory

Benchmark.memory

  • MemoryProfiler を使用して、処理の実行時のメモリ使用量やオブジェクトの割り当て (allocated)・保持 (retained) 数を計測する。
  • benchmark-ips とインターフェイスが似ており、使いやすい。
require 'benchmark/memory'

Benchmark.memory do |x|
  x.report('String#match?') { '🐶🐱🐰'.match?(/🐰\z/) }
  x.report('String#end_with?') { '🐶🐱🐰'.end_with?('🐰') }
  
  x.compare!
end
Calculating -------------------------------------
       String#match?    40.000  memsize (     0.000  retained)
                         1.000  objects (     0.000  retained)
                         1.000  strings (     0.000  retained)
    String#end_with?    80.000  memsize (     0.000  retained)
                         2.000  objects (     0.000  retained)
                         2.000  strings (     0.000  retained)

Comparison:
       String#match?:         40 allocated
    String#end_with?:         80 allocated - 2.00x more

まとめ

参考

4
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1