Skip to content

Instantly share code, notes, and snippets.

@tristang
Created September 2, 2016 13:50
Show Gist options
  • Save tristang/a961fdc327e7d79439729a6c381227dc to your computer and use it in GitHub Desktop.
Save tristang/a961fdc327e7d79439729a6c381227dc to your computer and use it in GitHub Desktop.
OpenStruct vs Struct vs Hash performance
require 'benchmark'
require 'ostruct'
REP = 1000000
User = Struct.new(:name, :age)
USER = "User".freeze
AGE = 21
HASH = {:name => USER, :age => AGE}.freeze
Benchmark.bm 20 do |x|
x.report 'OpenStruct slow' do
REP.times do |index|
OpenStruct.new(:name => "User", :age => 21)
end
end
x.report 'OpenStruct fast' do
REP.times do |index|
OpenStruct.new(HASH)
end
end
x.report 'Struct slow' do
REP.times do |index|
User.new("User", 21)
end
end
x.report 'Struct fast' do
REP.times do |index|
User.new(USER, AGE)
end
end
x.report 'Hash slow' do
REP.times do |index|
{:name => 'User', :age => 21}
end
end
x.report 'Hash fast' do
REP.times do |index|
{:name => USER, :age => AGE}
end
end
end
user system total real
OpenStruct slow 8.150000 0.000000 8.150000 ( 8.153797)
OpenStruct fast 7.980000 0.000000 7.980000 ( 7.983929)
Struct slow 0.270000 0.000000 0.270000 ( 0.268514)
Struct fast 0.200000 0.000000 0.200000 ( 0.196950)
Hash slow 0.580000 0.000000 0.580000 ( 0.584160)
Hash fast 0.460000 0.000000 0.460000 ( 0.458502)
@skrix
Copy link

skrix commented Jan 10, 2020

ruby 2.6.3 looks like hashes is faster than structs...

                           user     system      total        real
OpenStruct slow        0.760257   0.002922   0.763179 (  0.766513)
OpenStruct fast        0.597584   0.002368   0.599952 (  0.602767)
Struct slow            0.257806   0.003551   0.261357 (  0.265820)
Struct fast            0.220372   0.000379   0.220751 (  0.221160)
Hash slow              0.162665   0.001523   0.164188 (  0.166065)
Hash fast              0.128237   0.000906   0.129143 (  0.130054)

@gffcoutinho
Copy link

Ruby 3.0.2

                           user     system      total        real
OpenStruct slow        8.047000   0.000000   8.047000 (  8.061700)
OpenStruct fast        8.328000   0.016000   8.344000 (  8.341365)
Struct slow            0.157000   0.000000   0.157000 (  0.150189)
Struct fast            0.125000   0.000000   0.125000 (  0.125478)
Hash slow              0.093000   0.000000   0.093000 (  0.092687)
Hash fast              0.078000   0.000000   0.078000 (  0.077509)

@gildemberg-santos
Copy link

Ruby 2.5.1

                           user     system      total        real
OpenStruct slow        0.796528   0.000174   0.796702 (  0.800543)
OpenStruct fast        0.579909   0.000018   0.579927 (  0.582189)
Struct slow            0.152336   0.000000   0.152336 (  0.152991)
Struct fast            0.132024   0.000000   0.132024 (  0.132460)
Hash slow              0.217203   0.000000   0.217203 (  0.217984)
Hash fast              0.205297   0.000000   0.205297 (  0.205531)

@azrazalea-debtbook
Copy link

Ruby 3.2.2 arm64 macbook m1:

                           user     system      total        real
OpenStruct slow        5.198411   0.017713   5.216124 (  5.216191)
OpenStruct fast        5.079115   0.015835   5.094950 (  5.094971)
Struct slow            0.123186   0.000471   0.123657 (  0.123657)
Struct fast            0.103163   0.000209   0.103372 (  0.103370)
Hash slow              0.079912   0.000418   0.080330 (  0.080330)
Hash fast              0.070340   0.000254   0.070594 (  0.070595)

@azrazalea-debtbook
Copy link

In case anyone is interested, I added Data to the benchmarks and also included having to access each object 10 times. This shows Struct and Data outperforming Hash if you include that they access the data faster.

https://gist.github.com/azrazalea-debtbook/ae2d46eb43e5fec421da739461484577

                           user     system      total        real
OpenStruct slow        7.414383   0.022696   7.437079 (  7.437549)
OpenStruct fast        7.375381   0.024232   7.399613 (  7.433433)
Struct slow            0.723688   0.002673   0.726361 (  0.726363)
Struct fast            0.698972   0.002602   0.701574 (  0.701566)
Hash slow              1.162884   0.004929   1.167813 (  1.204769)
Hash fast              1.114190   0.003281   1.117471 (  1.119175)
Data slow              0.825655   0.002959   0.828614 (  0.828610)
Data fast              0.803010   0.002993   0.806003 (  0.806002)

@Rhoxio
Copy link

Rhoxio commented Aug 7, 2024

Ruby 3.3.4 on arm64 Macbook M3:

                           user     system      total        real
OpenStruct slow        3.804766   0.029756   3.834522 (  3.841408)
OpenStruct fast        3.601886   0.008229   3.610115 (  3.615765)
Struct slow            0.093334   0.000282   0.093616 (  0.093774)
Struct fast            0.081255   0.000151   0.081406 (  0.081472)
Hash slow              0.058589   0.000235   0.058824 (  0.058935)
Hash fast              0.047965   0.000229   0.048194 (  0.048267)

@azrazalea-debtbook
Copy link

Ruby 3.3.3 on arm64 Macbook M3 pro (on battery):

                           user     system      total        real
OpenStruct slow        4.448626   0.066791   4.515417 (  4.547827)
OpenStruct fast        4.273663   0.033246   4.306909 (  4.323059)
Struct slow            0.236373   0.001003   0.237376 (  0.237390)
Struct fast            0.228933   0.000859   0.229792 (  0.229795)
Hash slow              0.295569   0.001394   0.296963 (  0.296973)
Hash fast              0.265346   0.001351   0.266697 (  0.266701)
Data slow              0.350167   0.001732   0.351899 (  0.351904)
Data fast              0.342578   0.001556   0.344134 (  0.344140)

@mikodagatan
Copy link

I changed the code to use benchmark-ips so that it's easier to read. I've also moved around and order fast first before the slow one, then moved the hashes to the top to make it the reference point.

Here's the code:

require 'benchmark/ips'
require 'ostruct'

User = Struct.new(:name, :age)

USER = "User".freeze
AGE = 21
HASH = {:name => USER, :age => AGE}.freeze

Benchmark.ips do |x|
  x.report('Hash fast') do
    {:name => USER, :age => AGE}
  end

  x.report('Hash slow') do
    {:name => 'User', :age => 21}
  end

  x.report('OpenStruct fast') do
    OpenStruct.new(HASH)
  end

  x.report('OpenStruct slow') do
    OpenStruct.new(:name => "User", :age => 21)
  end

  x.report('Struct fast') do
    User.new(USER, AGE)
  end

  x.report('Struct slow') do
    User.new("User", 21)
  end

  x.compare!(order: :baseline)
end

Here's the result for Ruby 3.4.1 on arm64 Macbook M4 Max (plugged):

ruby 3.4.2 (2025-02-15 revision d2930f8e7a) +PRISM [arm64-darwin24]
Warming up --------------------------------------
           Hash fast     2.128M i/100ms
           Hash slow     1.705M i/100ms
     OpenStruct fast    27.331k i/100ms
     OpenStruct slow    26.523k i/100ms
         Struct fast     1.237M i/100ms
         Struct slow     1.032M i/100ms
Calculating -------------------------------------
           Hash fast     21.367M (± 0.9%) i/s   (46.80 ns/i) -    108.524M in   5.079385s
           Hash slow     17.100M (± 1.3%) i/s   (58.48 ns/i) -     86.976M in   5.087229s
     OpenStruct fast    274.690k (± 1.1%) i/s    (3.64 μs/i) -      1.394M in   5.074963s
     OpenStruct slow    267.433k (± 0.7%) i/s    (3.74 μs/i) -      1.353M in   5.058272s
         Struct fast     12.257M (± 1.6%) i/s   (81.58 ns/i) -     61.867M in   5.048580s
         Struct slow     10.328M (± 1.3%) i/s   (96.83 ns/i) -     52.629M in   5.096724s

Comparison:
           Hash fast: 21367245.1 i/s
           Hash slow: 17100063.3 i/s - 1.25x  slower
         Struct fast: 12257262.0 i/s - 1.74x  slower
         Struct slow: 10327899.6 i/s - 2.07x  slower
     OpenStruct fast:   274690.4 i/s - 77.79x  slower
     OpenStruct slow:   267432.6 i/s - 79.90x  slower

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