Skip to content

Instantly share code, notes, and snippets.

@taketo1113
Last active September 7, 2024 07:56
Show Gist options
  • Save taketo1113/3d74168f363e4d8d706a42b1e9800301 to your computer and use it in GitHub Desktop.
Save taketo1113/3d74168f363e4d8d706a42b1e9800301 to your computer and use it in GitHub Desktop.
Benchmark OpenStruct (ostruct)

Benchmark OpenStruct (ostruct)

It is benchmarks about OpenStruct.

The ostruct gem warn bellow message with Warning[:performance] = true

OpenStruct use is discouraged for performance reasons

ruby/ostruct#56

Benchmark Env

  • Mac mini (Apple M1)
  • OS: macOS 14.16
  • Ruby: 3.3.5 (+YJIT)

OpenStruct vs Struct

Code

Result

$ ruby --yjit benchmark_struct.rb 
ruby 3.3.5 (2024-09-03 revision ef084cc8f4) +YJIT [arm64-darwin23]
times: 10000
Rehearsal ----------------------------------------------------------
OpenStruct.new (args)    0.072869   0.001233   0.074102 (  0.074971)
OpenStruct.new (blank)   0.001441   0.000091   0.001532 (  0.001578)
Struct.new (args)        0.001368   0.000029   0.001397 (  0.001442)
OpenStruct (assign)      0.000917   0.000086   0.001003 (  0.001051)
Struct (assign)          0.000387   0.000012   0.000399 (  0.000452)
OpenStruct (read)        0.000825   0.000019   0.000844 (  0.000858)
Struct (read)            0.000358   0.000007   0.000365 (  0.000367)
------------------------------------------------- total: 0.079642sec

                             user     system      total        real
OpenStruct.new (args)    0.069159   0.000443   0.069602 (  0.069667)
OpenStruct.new (blank)   0.001309   0.000060   0.001369 (  0.001381)
Struct.new (args)        0.001212   0.000001   0.001213 (  0.001211)
OpenStruct (assign)      0.000685   0.000005   0.000690 (  0.000688)
Struct (assign)          0.000349   0.000001   0.000350 (  0.000348)
OpenStruct (read)        0.000733   0.000002   0.000735 (  0.000745)
Struct (read)            0.000305   0.000000   0.000305 (  0.000305)

OpenStruct vs method_missing

Code

Result

$ ruby --yjit benchmark_method_missing.rb
ruby 3.3.5 (2024-09-03 revision ef084cc8f4) +YJIT [arm64-darwin23]
times: 10000
Rehearsal -------------------------------------------------------------------------
OpenStruct.new (without args)           0.001591   0.000190   0.001781 (  0.001780)
MethodMissingClass.new (without args)   0.000912   0.000011   0.000923 (  0.000923)
OpenStruct (assign)                     0.000753   0.000028   0.000781 (  0.000781)
MethodMissingClass (assign)             0.003194   0.000066   0.003260 (  0.003263)
OpenStruct (read)                       0.000826   0.000021   0.000847 (  0.000848)
MethodMissingClass (read)               0.002592   0.000057   0.002649 (  0.002652)
---------------------------------------------------------------- total: 0.010241sec

                                            user     system      total        real
OpenStruct.new (without args)           0.001136   0.000023   0.001159 (  0.001158)
MethodMissingClass.new (without args)   0.000699   0.000004   0.000703 (  0.000702)
OpenStruct (assign)                     0.000655   0.000001   0.000656 (  0.000657)
MethodMissingClass (assign)             0.002662   0.000009   0.002671 (  0.002674)
OpenStruct (read)                       0.000727   0.000000   0.000727 (  0.000727)
MethodMissingClass (read)               0.002228   0.000021   0.002249 (  0.002248)
require 'benchmark'
require 'ostruct'
puts RUBY_DESCRIPTION
n = 10_000
puts "times: #{n}"
# OpenStruct
ostruct = OpenStruct.new
# method_missing
class MethodMissingClass
def method_missing(name, *value)
self.class.class_eval do
define_method "#{name}" do |*value|
if name.end_with?('=')
instance_variable_set("@#{name.to_s.chop}", *value)
else
instance_variable_get("@#{name}")
end
end
end
send(name, *value)
end
end
method_missing = MethodMissingClass.new
#method_missing.foo = 1
#method_missing.foo # => 1
#method_missing.hoge # => nil
# Benchmark
Benchmark.bmbm do |x|
x.report("OpenStruct.new (without args)") { n.times { OpenStruct.new } }
x.report("MethodMissingClass.new (without args)") { n.times { MethodMissingClass.new } }
x.report("OpenStruct (assign)") { n.times { ostruct.a = 1 } }
x.report("MethodMissingClass (assign)") { n.times { method_missing.a = 1 } }
x.report("OpenStruct (read)") { n.times { ostruct.a } }
x.report("MethodMissingClass (read)") { n.times { method_missing.a } }
end
require 'benchmark'
require 'ostruct'
puts RUBY_DESCRIPTION
n = 10_000
puts "times: #{n}"
# OpenStruct
ostruct = OpenStruct.new
# Struct
class MyStruct < Struct.new(:a, :b, :c); end
mystruct = MyStruct.new
# Benchmark
Benchmark.bmbm do |x|
x.report("OpenStruct.new (args)") { n.times { OpenStruct.new a: 1, b: 2, c: 3 } }
x.report("OpenStruct.new (blank)") { n.times { OpenStruct.new } }
x.report("Struct.new (args)") { n.times { MyStruct.new 1, 2, 3 } }
x.report("OpenStruct (assign)") { n.times { ostruct.a = 1 } }
x.report("Struct (assign)") { n.times { mystruct.a = 1 } }
x.report("OpenStruct (read)") { n.times { ostruct.a } }
x.report("Struct (read)") { n.times { mystruct.a } }
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment