Benchmark your ruby code

October 11, 2024

Ruby offers an easy way to benchmark the code. Here is some syntax for basic benchmarking.

This is done with the Benchmark module included in the Ruby standard library. You can run it even in IRB, simply require "benchmark".
In its simplest form, Benchmark.measure accepts a code block and outputs the time it takes to execute it.

require "benchmark"

puts Benchmark.measure { sleep(1) }

Returning:

  0.000061   0.000031   0.000092 (  1.001175)

The meaning of these stats (measured unit is second):

user CPU time   system CPU time   sum user + system CPU   times elapsed real time
  0.000061        0.000031          0.000092                (  1.001175)

A more advanced form is using Benchmark.bm, this allows us to compare the execution of different code blocks:

require "benchmark"

arr = (1..100_000).map { rand }
Benchmark.bm do |x|
  # Each x.report is a different test item to compare against
  x.report { arr.dup.sort }
  x.report { arr.dup.sort! }
end
   user     system      total        real
0.021121   0.001285   0.022406 (  0.022459)
0.018150   0.003547   0.021697 (  0.021704)

We can also label the reports x.report("sort").
Also, we can provide predefined methods in order to compare them.
Using Benchmark.bmbm will run the tests twice for a (supposedly) better reading.

require "benchmark"

arr = (1..100_000_000).map { rand }
def first_method(arr)
  arr.last
end

def second_method(arr)
  arr[-1]
end

Benchmark.bmbm do |x|
  x.report("first_method") { 100_000.times do; first_method(arr); end }
  x.report("second_method") { 100_000.times do; second_method(arr); end }
end
Rehearsal -------------------------------------------------
first_method    0.004724   0.000000   0.004724 (  0.004793)
second_method   0.004317   0.000000   0.004317 (  0.004381)
---------------------------------------- total: 0.009041sec

                    user     system      total        real
first_method    0.005145   0.000000   0.005145 (  0.005263)
second_method   0.004220   0.000000   0.004220 (  0.004278)

Benchmark-ips

Another performance gem built on the Benchmark from above. Benchmark-ips measure how many times a code block will run in a second (iterations per second - IPS) rather than measuring the time it takes for a code block to run.

You have to install the gem: gem install benchmark-ips. The syntax is:

require "benchmark/ips"

arr = (1..100_000_000).map { rand }
def first_method(arr)
  arr.last
end

def second_method(arr)
  arr[-1]
end

Benchmark.ips do |x|
  x.report("first method") { first_method(arr) }
  x.report("second method") { second_method(arr) }

  x.compare!
end
ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [x86_64-linux]
Warming up --------------------------------------
        first method     1.685M i/100ms
       second method     1.921M i/100ms
Calculating -------------------------------------
        first method     16.390M (± 3.2%) i/s   (61.01 ns/i) -     82.542M in   5.042099s
       second method     18.257M (± 1.1%) i/s   (54.77 ns/i) -     92.216M in   5.051680s

Comparison:
       second method: 18256810.3 i/s
        first method: 16389785.2 i/s - 1.11x  slower

Recommended read: https://shopify.engineering/how-fix-slow-code-ruby.