Skip to content

Instantly share code, notes, and snippets.

@manuphatak
Last active November 17, 2020 04:18
Show Gist options
  • Save manuphatak/9ef6ba81743300ca57e93078aecc4222 to your computer and use it in GitHub Desktop.
Save manuphatak/9ef6ba81743300ca57e93078aecc4222 to your computer and use it in GitHub Desktop.
# Response to:
# http://blog.honeybadger.io/how-openstruct-and-hashes-can-kill-performance/
#
# It's not faire to use `Hash.new.merge(data)` if you can `Hash[data]`.
# `Hash[data]` is way faster! Lets compare!
#
# Read more: http://ruby-doc.org/core-2.2.0/Hash.html#method-c-5B-5D
#
# [UPDATE]
#
# * Disable GC for each report
require 'benchmark/ips'
require 'ostruct'
# Enable and start GC before each job run. Disable GC afterwards.
#
# Inspired by https://www.omniref.com/ruby/2.2.1/symbols/Benchmark/bm?#annotation=4095926&line=182
class GCSuite
def warming(*)
run_gc
end
def running(*)
run_gc
end
def warmup_stats(*)
end
def add_report(*)
end
private
def run_gc
GC.enable
GC.start
GC.disable
end
end
suite = GCSuite.new
data = { x: 100, y: 200 }
PointStruct = Struct.new(:x, :y)
PointKeywordStruct = Struct.new(:x, :y, keyword_init: true)
class FakedPointStruct
attr_accessor :x, :y
def initialize(x, y)
@x = x
@y = y
end
end
class PointClassFetch
attr_accessor :x, :y
def initialize(args)
@x = args.fetch(:x) # NOTE: Hash#fetch -> performance impact
@y = args.fetch(:y)
end
end
class PointClassKeyword
attr_accessor :x, :y
def initialize(x:, y:)
@x = x
@y = y
end
end
puts "\n\nINITIALIZATION =========="
Benchmark.ips do |x|
x.json! 'INITIALIZATION.json'
x.config(suite: suite)
# Create Objects as a reference value
x.report("Object.new") { Object.new }
x.report("FakedPointStruct") { FakedPointStruct.new(100, 200) }
x.report("PointStruct") { PointStruct.new(100, 200) }
x.report("PointKeywordStruct") { PointKeywordStruct.new(data) }
x.report("Hash[]") { Hash[data] }
x.report("PointClassFetch") { PointClassFetch.new(data) }
x.report("PointClassKeyword") { PointClassKeyword.new(data) }
x.report("Hash#merge") { Hash.new.merge(data) }
x.report("OpenStruct") { OpenStruct.new(data) }
x.compare!
end
puts "\n\nREAD =========="
point_struct = PointStruct.new(100, 200)
point_keyword_struct = PointKeywordStruct.new(data)
point_class_fetch = PointClassFetch.new(data)
point_class_keyword = PointClassKeyword.new(data)
point_hash = Hash[data]
point_open_struct = OpenStruct.new(data)
Benchmark.ips do |x|
x.json! 'READ.json'
x.config(suite: suite)
x.report("PointStruct") { point_struct.x }
x.report("PointKeywordStruct") { point_keyword_struct.x }
x.report("PointClassFetch") { point_class_fetch.x }
x.report("PointClassKeyword") { point_class_keyword.x }
x.report("Hash#fetch") { point_hash.fetch(:x) }
x.report("Hash#[]") { point_hash[:x] }
x.report("OpenStruct") { point_open_struct.x }
x.compare!
end
puts "\n\nWRITE =========="
Benchmark.ips do |x|
x.json! 'WRITE.json'
x.config(suite: suite)
x.report("PointStruct") { point_struct.x = 1 }
x.report("PointKeywordStruct") { point_keyword_struct.x = 1 }
x.report("PointClassFetch") { point_class_fetch.x = 1 }
x.report("PointClassKeyword") { point_class_keyword.x = 1 }
x.report("Hash") { point_hash[:x] = 1 }
x.report("OpenStruct") { point_open_struct.x = 1 }
x.compare!
end
# INITIALIZATION ==========
# Calculating -------------------------------------
# Object.new 232.512k i/100ms
# FakedPointStruct 208.718k i/100ms
# PointStruct 213.914k i/100ms
# PointKeywordStruct 175.460k i/100ms
# Hash[] 183.239k i/100ms
# PointClassFetch 182.943k i/100ms
# PointClassKeyword 131.129k i/100ms
# Hash#merge 154.966k i/100ms
# OpenStruct 117.413k i/100ms
# -------------------------------------------------
# Object.new 7.677M (±16.1%) i/s - 37.667M
# FakedPointStruct 5.919M (±12.3%) i/s - 29.221M
# PointStruct 5.801M (±11.4%) i/s - 28.664M
# PointKeywordStruct 4.125M (± 8.9%) i/s - 20.529M
# Hash[] 4.421M (±17.4%) i/s - 21.622M
# PointClassFetch 4.810M (± 7.0%) i/s - 23.966M
# PointClassKeyword 2.308M (±10.0%) i/s - 11.539M
# Hash#merge 2.963M (±14.7%) i/s - 14.567M
# OpenStruct 1.975M (± 6.6%) i/s - 9.863M
# Comparison:
# Object.new: 7677097.4 i/s
# FakedPointStruct: 5918849.2 i/s - 1.30x slower
# PointStruct: 5801076.3 i/s - 1.32x slower
# PointClassFetch: 4809859.9 i/s - 1.60x slower
# Hash[]: 4420989.7 i/s - 1.74x slower
# PointKeywordStruct: 4125176.9 i/s - 1.86x slower
# Hash#merge: 2962687.0 i/s - 2.59x slower
# PointClassKeyword: 2307777.5 i/s - 3.33x slower
# OpenStruct: 1974635.6 i/s - 3.89x slower
# READ ==========
# Calculating -------------------------------------
# PointStruct 275.075k i/100ms
# PointKeywordStruct 284.487k i/100ms
# PointClassFetch 284.814k i/100ms
# PointClassKeyword 274.721k i/100ms
# Hash#fetch 262.639k i/100ms
# Hash#[] 282.053k i/100ms
# OpenStruct 244.214k i/100ms
# -------------------------------------------------
# PointStruct 19.828M (± 5.1%) i/s - 99.027M
# PointKeywordStruct 20.033M (± 5.2%) i/s - 100.139M
# PointClassFetch 23.465M (± 3.4%) i/s - 117.343M
# PointClassKeyword 22.488M (± 2.2%) i/s - 112.636M
# Hash#fetch 16.465M (± 2.0%) i/s - 82.469M
# Hash#[] 22.144M (± 3.0%) i/s - 110.847M
# OpenStruct 10.675M (± 3.5%) i/s - 53.483M
# Comparison:
# PointClassFetch: 23465137.5 i/s
# PointClassKeyword: 22488166.1 i/s - 1.04x slower
# Hash#[]: 22143937.7 i/s - 1.06x slower
# PointKeywordStruct: 20033487.9 i/s - 1.17x slower
# PointStruct: 19827584.5 i/s - 1.18x slower
# Hash#fetch: 16465062.4 i/s - 1.43x slower
# OpenStruct: 10674642.1 i/s - 2.20x slower
# WRITE ==========
# Calculating -------------------------------------
# PointStruct 263.792k i/100ms
# PointKeywordStruct 265.678k i/100ms
# PointClassFetch 269.824k i/100ms
# PointClassKeyword 282.345k i/100ms
# Hash 267.091k i/100ms
# OpenStruct 224.234k i/100ms
# -------------------------------------------------
# PointStruct 17.523M (± 2.3%) i/s - 87.579M
# PointKeywordStruct 17.653M (± 3.0%) i/s - 88.205M
# PointClassFetch 21.269M (± 4.6%) i/s - 106.041M
# PointClassKeyword 21.139M (± 3.7%) i/s - 105.597M
# Hash 16.899M (± 4.7%) i/s - 84.401M
# OpenStruct 7.053M (± 5.8%) i/s - 35.205M
# Comparison:
# PointClassFetch: 21268766.9 i/s
# PointClassKeyword: 21138642.2 i/s - 1.01x slower
# PointKeywordStruct: 17652879.3 i/s - 1.20x slower
# PointStruct: 17522748.0 i/s - 1.21x slower
# Hash: 16899040.6 i/s - 1.26x slower
# OpenStruct: 7053426.1 i/s - 3.02x slower
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment