Last active
November 17, 2020 04:18
-
-
Save manuphatak/9ef6ba81743300ca57e93078aecc4222 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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