Skip to content

Instantly share code, notes, and snippets.

@goldeneggg
Last active November 28, 2022 01:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save goldeneggg/9ee48fa376fa1f81cf324f64ca4ca97e to your computer and use it in GitHub Desktop.
Save goldeneggg/9ee48fa376fa1f81cf324f64ca4ca97e to your computer and use it in GitHub Desktop.
Benchmark Comparison - Hash vs Struct vs OpenStruct in Ruby 2.7.6
require 'benchmark/ips'
require 'benchmark/memory'
module Benchmarker
class Target
attr_reader :title, :code_block
def initialize(title:, code_block:)
@title = title
@code_block = code_block
end
end
class IpsConf
DEFAULT_TIME = 5
DEFAULT_WARMUP = 2
attr_reader :time, :warmup, :suite, :result_holder, :result_saver
def initialize(time: DEFAULT_TIME,
warmup: DEFAULT_WARMUP,
suite: nil,
result_holder: nil,
result_saver: nil)
@time = time
@warmup = warmup
# See: https://github.com/evanphx/benchmark-ips#custom-suite
@suite = suite
# See: https://github.com/evanphx/benchmark-ips#independent-benchmarking
@result_holder = result_holder
@result_saver = result_saver
end
end
class MemoryConf
attr_reader :result_holder
def initialize(result_holder: nil)
# See: https://github.com/michaelherold/benchmark-memory#hold-results-between-invocations
@result_holder = result_holder
end
end
class << self
def compare_ips(*targets, ipsconf: nil)
ic =
if ipsconf.nil?
IpsConf.new
else
ipsconf
end
Benchmark.ips do |x|
x.time = ic.time
x.warmup = ic.warmup
x.config(suite: ic.suite) unless ic.suite.nil?
targets.each do |target|
x.report(target.title) { target.code_block.call }
end
x.hold!(ic.result_holder) unless ic.result_holder.nil?
x.save!(ic.result_saver) unless ic.result_saver.nil?
x.compare!
end
end
def compare_memory(*targets, memoryconf: nil)
mc =
if memoryconf.nil?
MemoryConf.new
else
memoryconf
end
Benchmark.memory do |x|
targets.each do |target|
x.report(target.title) { target.code_block.call }
end
x.hold!(mc.result_holder) unless mc.result_holder.nil?
x.compare!
end
end
def compare(*targets, ipsconf: nil, memoryconf: nil)
compare_ips(*targets, ipsconf: ipsconf)
compare_memory(*targets, memoryconf: memoryconf)
end
end
end
User = Struct.new(:name, :age)
t1 = ::Benchmarker::Target.new(
title: 't1 is OpenStruct',
code_block: proc do
OpenStruct.new(:name => 'User', :age => 21)
end
)
t2 = ::Benchmarker::Target.new(
title: 't2 is Struct',
code_block: proc do
User.new('User', 21)
end
)
t3 = ::Benchmarker::Target.new(
title: 't3 is Hash',
code_block: proc do
{:name => 'User', :age => 21}
end
)
Benchmarker.compare(t1, t2, t3)
@goldeneggg
Copy link
Author

goldeneggg commented Nov 24, 2022

Warming up --------------------------------------
    t1 is OpenStruct   136.133k i/100ms
        t2 is Struct   380.290k i/100ms
          t3 is Hash   570.684k i/100ms
Calculating -------------------------------------
    t1 is OpenStruct      1.447M (± 7.3%) i/s -      7.215M in   5.024248s
        t2 is Struct      3.571M (± 3.8%) i/s -     17.874M in   5.013054s
          t3 is Hash      5.774M (± 5.0%) i/s -     29.105M in   5.055118s

Comparison:
          t3 is Hash:  5774185.0 i/s
        t2 is Struct:  3571276.0 i/s - 1.62x  (± 0.00) slower
    t1 is OpenStruct:  1446900.6 i/s - 3.99x  (± 0.00) slower

Calculating -------------------------------------
    t1 is OpenStruct   416.000  memsize (     0.000  retained)
                         4.000  objects (     0.000  retained)
                         1.000  strings (     0.000  retained)
        t2 is Struct    80.000  memsize (     0.000  retained)
                         2.000  objects (     0.000  retained)
                         1.000  strings (     0.000  retained)
          t3 is Hash   208.000  memsize (     0.000  retained)
                         2.000  objects (     0.000  retained)
                         1.000  strings (     0.000  retained)

Comparison:
        t2 is Struct:         80 allocated
          t3 is Hash:        208 allocated - 2.60x more
    t1 is OpenStruct:        416 allocated - 5.20x more

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment