↖️ Show all posts

Today I learned in Rubyland

Benchmarking my Ruby code shed light on a few things I didn’t know before about .nil? and Set#include?.

Boolean operations nil? vs ==

.nil? makes the code more readable, but it’s slowing things down.

require 'benchmark'

n = 1_000_000
Benchmark.bm do |x|
  x.report('nil?') { n.times { nil.nil? } }
  x.report('==') { n.times { nil == nil } }
end

[#<Benchmark::Tms:0x000000011ee2b1d8
  @cstime=0.0,
  @cutime=0.0,
  @label="nil?",
  @real=0.046839999999065185,
  @stime=0.0005310000000000037,
  @total=0.046846,
  @utime=0.046314999999999995>,
 #<Benchmark::Tms:0x000000011ee2b098
  @cstime=0.0,
  @cutime=0.0,
  @label="==",
  @real=0.033373000000210595,
  @stime=0.00028000000000000247,
  @total=0.033374000000000015, πŸ‘‘πŸ‘‘πŸ‘‘
  @utime=0.03309400000000001>]

Take a look at this:

require 'benchmark'

h = {a: 1, b: {c: 2}}
n = 1_000_000
Benchmark.bm do |x|
  x.report('nil?') { n.times { h.dig(:b, :d).nil? } }
  x.report('==') { n.times { h.dig(:b, :d) == nil } }
end

[#<Benchmark::Tms:0x0000000122c0aaa8
  @cstime=0.0,
  @cutime=0.0,
  @label="nil?",
  @real=0.09058699999877717,
  @stime=0.0007450000000000095,
  @total=0.09056200000000002,
  @utime=0.08981700000000001>,
 #<Benchmark::Tms:0x0000000122c0a968
  @cstime=0.0,
  @cutime=0.0,
  @label="==",
  @real=0.07575299999734852,
  @stime=0.0001769999999999966,
  @total=0.075752, πŸ‘‘πŸ‘‘πŸ‘‘
  @utime=0.075575>] 

Ruby’s Set class performance

The performance of the Set class, can be worse than building a hash lookup table in some cases.

Take a look at this:

require 'benchmark'

s_array = Set[[1, 2], [3, 4], [5, 6]]
s_hash = Set[{x: 1, y: 2}, {x: 3, y: 4}, {x: 5, y: 6}]
s_string = Set['1_2', '3_4', '5_6']
h = {1 => {2 => true}, 3 => {4 => true}, 5 => {6 => true}}
n = 1_000_000
Benchmark.bm do |x|
  x.report('Set#include? Array') { n.times { s_array.include?([1, 2]) } }
  x.report('Set#include? Hash') { n.times { s_hash.include?({x: 1, y: 2}) } }
  x.report('Set#include? String') { n.times { s_string.include?("1_2") } }
  x.report('[]') { n.times { h.dig(1, 2) } }
end

[#<Benchmark::Tms:0x000000011ee4ff60
  @cstime=0.0,
  @cutime=0.0,
  @label="Set#include? Array",
  @real=0.3224740000005113,
  @stime=0.0011849999999999916,
  @total=0.3223769999999999,
  @utime=0.3211919999999999>,
 #<Benchmark::Tms:0x000000011ee4fd30
  @cstime=0.0,
  @cutime=0.0,
  @label="Set#include? Hash",
  @real=0.4202399999994668,
  @stime=0.0027610000000000134,
  @total=0.4201859999999997,
  @utime=0.4174249999999997>,
 #<Benchmark::Tms:0x000000011ee4fbf0
  @cstime=0.0,
  @cutime=0.0,
  @label="Set#include? String",
  @real=0.08109800000238465,
  @stime=0.0010399999999999299,
  @total=0.0810949999999997, πŸš€πŸš€πŸš€
  @utime=0.08005499999999977>,
 #<Benchmark::Tms:0x000000011ee4fa60
  @cstime=0.0,
  @cutime=0.0,
  @label="[]",
  @real=0.0856240000030084,
  @stime=0.0006800000000000139,
  @total=0.08562099999999973, πŸš€
  @utime=0.08494099999999971>]

Depending on the use case, it might be better to use a hash lookup table instead of a Set.
If you’re working with strings, the Set class is the best choice!


Read next ➑️