Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/bkuhlmann/benchmarks

A collection of micro benchmarks.
https://github.com/bkuhlmann/benchmarks

benchmark micro speed

Last synced: 2 months ago
JSON representation

A collection of micro benchmarks.

Awesome Lists containing this project

README

        

:toc: macro
:toclevels: 5
:figure-caption!:

= Benchmarks

Benchmarks is a collection of Ruby micro benchmarks which can be cloned and run locally or used as
an information point of reference. The various statistics on Ruby performance captured within this
project may or may not surprise you.

toc::[]

== Features

* Uses link:https://github.com/evanphx/benchmark-ips[Benchmark IPS] to calculate CPU/speed results.
* Each script is independently executable.

== Requirements

. link:https://www.ruby-lang.org[Ruby]

== Setup

To install, run:

[source,bash]
----
git clone https://github.com/bkuhlmann/benchmarks.git
cd benchmarks
git checkout 4.9.0
bin/setup
----

== Usage

All benchmark scripts are found within the `scripts` folder and you can run any benchmark using a relative or absolute file path. Example:

=== scripts/arrays/concatenation

*Source*

[source,ruby]
----
#! /usr/bin/env ruby
# frozen_string_literal: true

require "bundler/inline"

gemfile true do
source "https://rubygems.org"

gem "benchmark-ips"
end

a = %w[one two three]
b = %w[four five six]

Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2

benchmark.report "#+" do
a + b
end

benchmark.report "#+=" do
duplicate = a.dup
duplicate += b
end

benchmark.report "#concat" do
a.dup.concat b
end

benchmark.report "#|" do
a | b
end

benchmark.report "#<< + #flatten" do
(a.dup << b).flatten
end

benchmark.report "splat + #flatten" do
[a, *b].flatten
end

benchmark.report "multi-splat" do
[*a, *b]
end

benchmark.compare!
end
----

*Benchmark*

....
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0]
Warming up --------------------------------------
#+ 1.109M i/100ms
#+= 642.208k i/100ms
#concat 640.592k i/100ms
#| 462.891k i/100ms
#<< + #flatten 190.264k i/100ms
splat + #flatten 191.960k i/100ms
multi-splat 595.693k i/100ms
Calculating -------------------------------------
#+ 11.380M (± 6.3%) i/s - 57.673M in 5.088056s
#+= 7.020M (± 7.5%) i/s - 35.321M in 5.060801s
#concat 6.757M (± 4.3%) i/s - 33.951M in 5.033526s
#| 4.589M (± 7.1%) i/s - 23.145M in 5.069256s
#<< + #flatten 1.995M (± 7.8%) i/s - 10.084M in 5.083410s
splat + #flatten 1.987M (± 7.7%) i/s - 9.982M in 5.053069s
multi-splat 6.018M (± 5.4%) i/s - 30.380M in 5.063797s

Comparison:
#+: 11379928.4 i/s
#+=: 7020046.0 i/s - 1.62x slower
#concat: 6757373.6 i/s - 1.68x slower
multi-splat: 6017673.7 i/s - 1.89x slower
#|: 4588614.9 i/s - 2.48x slower
#<< + #flatten: 1995390.6 i/s - 5.70x slower
splat + #flatten: 1986837.6 i/s - 5.73x slower
....

=== scripts/arrays/search

*Source*

[source,ruby]
----
#! /usr/bin/env ruby
# frozen_string_literal: true

require "bundler/inline"

gemfile true do
source "https://rubygems.org"

gem "benchmark-ips"
end

list = %w[one two three four five six seven eight nine ten]
pattern = /t/

Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2

benchmark.report("#grep") { list.grep pattern }
benchmark.report("#select") { list.select { |value| value.match? pattern } }

benchmark.compare!
end
----

*Benchmark*

....
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0]
Warming up --------------------------------------
#grep 173.406k i/100ms
#select 185.779k i/100ms
Calculating -------------------------------------
#grep 1.771M (± 6.7%) i/s - 8.844M in 5.016015s
#select 1.912M (± 5.5%) i/s - 9.661M in 5.067631s

Comparison:
#select: 1912344.1 i/s
#grep: 1770866.9 i/s - same-ish: difference falls within error
....

=== scripts/bindings

*Source*

[source,ruby]
----
#! /usr/bin/env ruby
# frozen_string_literal: true

require "bundler/inline"

gemfile true do
source "https://rubygems.org"
gem "benchmark-ips"
end

module Test
def self.with_binding(end:) = binding.local_variable_get(:end)

def self.with_pinning(end:) = {end:}[:end]
end

Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2

benchmark.report("Binding") { Test.with_binding end: 1 }
benchmark.report("Pinning") { Test.with_pinning end: 1 }

benchmark.compare!
end
----

*Benchmark*

....
ruby 3.3.4 (2024-07-09 revision be1089c8ec) +YJIT [arm64-darwin23.5.0]
Warming up --------------------------------------
Binding 761.490k i/100ms
Pinning 2.104M i/100ms
Calculating -------------------------------------
Binding 7.991M (± 2.0%) i/s - 40.359M in 5.052741s
Pinning 24.189M (± 1.3%) i/s - 122.019M in 5.045416s

Comparison:
Pinning: 24188558.7 i/s
Binding: 7990579.0 i/s - 3.03x slower
....

=== scripts/closures

*Source*

[source,ruby]
----
#! /usr/bin/env ruby
# frozen_string_literal: true

require "bundler/inline"

gemfile true do
source "https://rubygems.org"

gem "benchmark-ips"
end

Example = Class.new do
def echo_implicit text
yield
text
end

def echo_implicit_guard text
yield if block_given?
text
end

def echo_explicit text, &block
yield block
text
end

def echo_explicit_guard text, &block
yield block if block
text
end
end

block_example = Example.new
lambda_example = -> text { text }
proc_example = proc { |text| text }

Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2

benchmark.report "Block (implicit)" do
block_example.echo_implicit("hi") { "test" }
end

benchmark.report "Block (implicit guard)" do
block_example.echo_implicit_guard("hi") { "test" }
end

benchmark.report "Block (explicit)" do
block_example.echo_explicit("hi") { "test" }
end

benchmark.report "Block (explicit guard)" do
block_example.echo_explicit_guard("hi") { "test" }
end

benchmark.report "Lambda" do
lambda_example.call "test"
end

benchmark.report "Proc" do
proc_example.call "test"
end

benchmark.compare!
end
----

*Benchmark*

....
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0]
Warming up --------------------------------------
Block (implicit) 3.719M i/100ms
Block (implicit guard)
3.997M i/100ms
Block (explicit) 595.448k i/100ms
Block (explicit guard)
597.963k i/100ms
Lambda 2.428M i/100ms
Proc 2.563M i/100ms
Calculating -------------------------------------
Block (implicit) 59.804M (± 1.0%) i/s - 301.203M in 5.037053s
Block (implicit guard)
59.083M (± 1.2%) i/s - 295.765M in 5.006599s
Block (explicit) 6.377M (± 6.6%) i/s - 32.154M in 5.065513s
Block (explicit guard)
6.365M (± 5.7%) i/s - 32.290M in 5.090170s
Lambda 34.880M (± 1.2%) i/s - 174.833M in 5.013192s
Proc 35.295M (± 1.9%) i/s - 176.865M in 5.012838s

Comparison:
Block (implicit): 59803662.6 i/s
Block (implicit guard): 59083369.8 i/s - same-ish: difference falls within error
Proc: 35294734.0 i/s - 1.69x slower
Lambda: 34879827.8 i/s - 1.71x slower
Block (explicit): 6377470.8 i/s - 9.38x slower
Block (explicit guard): 6365264.1 i/s - 9.40x slower
....

=== scripts/constants/lookup

*Source*

[source,ruby]
----
#! /usr/bin/env ruby
# frozen_string_literal: true

require "bundler/inline"

gemfile true do
source "https://rubygems.org"

gem "benchmark-ips"
end

CONSTANTS = Hash.new

module Constants
1_000.times { |index| CONSTANTS["EXAMPLE_#{index}"] = const_set "EXAMPLE_#{index}", index }
end

Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2

benchmark.report("#[]") { CONSTANTS["EXAMPLE_666"] }
benchmark.report("Module.get (symbol)") { Constants.const_get :EXAMPLE_666 }
benchmark.report("Module.get (string)") { Constants.const_get "EXAMPLE_666" }
benchmark.report("Object.get") { Object.const_get "Constants::EXAMPLE_666" }

benchmark.compare!
end
----

*Benchmark*

....
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0]
Warming up --------------------------------------
#[] 3.380M i/100ms
Module.get (symbol) 3.491M i/100ms
Module.get (string) 1.706M i/100ms
Object.get 1.068M i/100ms
Calculating -------------------------------------
#[] 44.355M (± 0.2%) i/s - 223.075M in 5.029360s
Module.get (symbol) 44.370M (± 0.1%) i/s - 223.435M in 5.035747s
Module.get (string) 18.818M (± 0.5%) i/s - 95.510M in 5.075490s
Object.get 11.076M (± 0.3%) i/s - 55.527M in 5.013445s

Comparison:
Module.get (symbol): 44369850.1 i/s
#[]: 44354632.8 i/s - same-ish: difference falls within error
Module.get (string): 18818346.2 i/s - 2.36x slower
Object.get: 11075724.0 i/s - 4.01x slower
....

=== scripts/delegates

*Source*

[source,ruby]
----
#! /usr/bin/env ruby
# frozen_string_literal: true

require "bundler/inline"

gemfile true do
source "https://rubygems.org"

gem "benchmark-ips"
end

require "delegate"
require "forwardable"

module Echo
def self.call(message) = message
end

class ForwardExample
def initialize operation
@operation = operation
end

def call(...) = operation.call(...)

private

attr_reader :operation
end

class DelegateExample
extend Forwardable

delegate %i[call] => :operation

def initialize operation
@operation = operation
end

private

attr_reader :operation
end

class SimpleExample < SimpleDelegator
end

class ClassExample < DelegateClass Echo
end

message = "A test."
forward_example = ForwardExample.new Echo
deletate_example = DelegateExample.new Echo
simple_example = SimpleExample.new Echo
class_example = ClassExample.new Echo

Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2

benchmark.report("Forward") { forward_example.call message }
benchmark.report("Delegate") { deletate_example.call message }
benchmark.report("Simple Delegator") { simple_example.call message }
benchmark.report("Delegate Class") { class_example.call message }

benchmark.compare!
end
----

*Benchmark*

....
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0]
Warming up --------------------------------------
Forward 1.132M i/100ms
Delegate 1.017M i/100ms
Simple Delegator 385.198k i/100ms
Delegate Class 381.099k i/100ms
Calculating -------------------------------------
Forward 13.380M (± 5.3%) i/s - 66.769M in 5.005398s
Delegate 12.486M (± 3.0%) i/s - 63.053M in 5.054667s
Simple Delegator 4.191M (± 7.0%) i/s - 21.186M in 5.077130s
Delegate Class 4.174M (± 6.5%) i/s - 20.960M in 5.041277s

Comparison:
Forward: 13380268.0 i/s
Delegate: 12486453.4 i/s - same-ish: difference falls within error
Simple Delegator: 4191347.1 i/s - 3.19x slower
Delegate Class: 4174068.4 i/s - 3.21x slower
....

=== scripts/hashes/lookup

*Source*

[source,ruby]
----
#! /usr/bin/env ruby
# frozen_string_literal: true

require "bundler/inline"

gemfile true do
source "https://rubygems.org"

gem "benchmark-ips"
end

example = {a: 1, b: 2, c: 3}

Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2

benchmark.report("#[]") { example[:b] }
benchmark.report("#fetch") { example.fetch :b }
benchmark.report("#fetch (default)") { example.fetch :b, "default" }
benchmark.report("#fetch (block)") { example.fetch(:b) { "default" } }
benchmark.report("#dig") { example.dig :b }

benchmark.compare!
end
----

*Benchmark*

....
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0]
Warming up --------------------------------------
#[] 4.035M i/100ms
#fetch 3.642M i/100ms
#fetch (default) 3.661M i/100ms
#fetch (block) 3.639M i/100ms
#dig 3.747M i/100ms
Calculating -------------------------------------
#[] 53.886M (± 0.1%) i/s - 270.328M in 5.016651s
#fetch 45.478M (± 0.1%) i/s - 229.440M in 5.045124s
#fetch (default) 45.677M (± 0.4%) i/s - 230.660M in 5.049861s
#fetch (block) 44.675M (± 0.2%) i/s - 225.636M in 5.050641s
#dig 50.390M (± 0.5%) i/s - 254.823M in 5.057135s

Comparison:
#[]: 53886256.9 i/s
#dig: 50390168.1 i/s - 1.07x slower
#fetch (default): 45677292.4 i/s - 1.18x slower
#fetch: 45477689.6 i/s - 1.18x slower
#fetch (block): 44674922.3 i/s - 1.21x slower
....

=== scripts/hashes/merge

*Source*

[source,ruby]
----
#! /usr/bin/env ruby
# frozen_string_literal: true

require "bundler/inline"

gemfile true do
source "https://rubygems.org"

gem "benchmark-ips"
end

extra = {b: 2}

Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2

benchmark.report("Splat") { {a: 1, **extra} }
benchmark.report("Merge") { {a: 1}.merge extra }
benchmark.report("Merge!") { {a: 1}.merge! extra }
benchmark.report("Dup Merge!") { {a: 1}.dup.merge! extra }

benchmark.compare!
end
----

*Benchmark*

....
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0]
Warming up --------------------------------------
Splat 977.474k i/100ms
Merge 651.230k i/100ms
Merge! 979.604k i/100ms
Dup Merge! 525.213k i/100ms
Calculating -------------------------------------
Splat 10.458M (± 7.0%) i/s - 52.784M in 5.072085s
Merge 6.637M (± 2.2%) i/s - 33.213M in 5.006663s
Merge! 10.624M (± 6.7%) i/s - 53.878M in 5.093783s
Dup Merge! 5.400M (± 7.0%) i/s - 27.311M in 5.081979s

Comparison:
Merge!: 10624192.6 i/s
Splat: 10457715.9 i/s - same-ish: difference falls within error
Merge: 6636870.8 i/s - 1.60x slower
Dup Merge!: 5399502.4 i/s - 1.97x slower
....

=== scripts/hashes/reduce

*Source*

[source,ruby]
----
#! /usr/bin/env ruby
# frozen_string_literal: true

require "bundler/inline"

gemfile true do
source "https://rubygems.org"

gem "benchmark-ips"
end

numbers = {
one: 1,
two: 2,
three: 3,
four: 4,
five: 5,
six: 6,
seven: 7,
eight: 8,
nine: 9,
ten: 10
}

Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2

benchmark.report "Reduce" do
numbers.reduce({}) { |collection, (key, value)| collection.merge! value => key }
end

benchmark.report "With Object" do
numbers.each.with_object({}) { |(key, value), collection| collection[value] = key }
end

benchmark.compare!
end
----

*Benchmark*

....
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0]
Warming up --------------------------------------
Reduce 46.696k i/100ms
With Object 86.950k i/100ms
Calculating -------------------------------------
Reduce 469.369k (± 7.1%) i/s - 2.381M in 5.099251s
With Object 893.855k (± 0.5%) i/s - 4.521M in 5.058420s

Comparison:
With Object: 893855.0 i/s
Reduce: 469368.6 i/s - 1.90x slower
....

=== scripts/loops

*Source*

[source,ruby]
----
#! /usr/bin/env ruby
# frozen_string_literal: true

require "bundler/inline"

gemfile true do
source "https://rubygems.org"

gem "benchmark-ips"
end

collection = (1..1_000).to_a
sum = 0

Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2

benchmark.report "for" do
for number in collection do
sum += number
end
end

benchmark.report "#each" do
collection.each { |number| sum += number }
end

benchmark.compare!
end
----

*Benchmark*

....
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0]
Warming up --------------------------------------
for 6.369k i/100ms
#each 6.583k i/100ms
Calculating -------------------------------------
for 63.745k (± 0.1%) i/s - 324.819k in 5.095574s
#each 65.617k (± 0.5%) i/s - 329.150k in 5.016325s

Comparison:
#each: 65617.4 i/s
for: 63745.4 i/s - 1.03x slower
....

=== scripts/methods/define_method

*Source*

[source,ruby]
----
#! /usr/bin/env ruby
# frozen_string_literal: true

require "bundler/inline"

gemfile true do
source "https://rubygems.org"

gem "benchmark-ips"
end

require "forwardable"

Person = Class.new do
def initialize first, last
@first = first
@last = last
end

def full_name
"#{first} #{last}"
end

private

attr_reader :first, :last
end

Example = Class.new Person do
extend Forwardable

define_method :unbound_full_name, Person.instance_method(:full_name)
delegate %i[full_name] => :person

def initialize first, last, person: Person.new(first, last)
super first, last
@person = person
end

def wrapped_full_name
person.full_name
end

private

attr_reader :first, :last, :person
end

example = Example.new "Jill", "Doe"

Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2

benchmark.report("Wrapped") { example.wrapped_full_name }
benchmark.report("Defined") { example.unbound_full_name }
benchmark.report("Delegated") { example.full_name }

benchmark.compare!
end
----

*Benchmark*

....
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0]
Warming up --------------------------------------
Wrapped 1.128M i/100ms
Defined 1.153M i/100ms
Delegated 626.507k i/100ms
Calculating -------------------------------------
Wrapped 12.039M (± 4.8%) i/s - 60.917M in 5.072433s
Defined 12.270M (± 3.9%) i/s - 62.244M in 5.080966s
Delegated 6.912M (± 6.2%) i/s - 34.458M in 5.005586s

Comparison:
Defined: 12269954.3 i/s
Wrapped: 12039424.1 i/s - same-ish: difference falls within error
Delegated: 6912331.3 i/s - 1.78x slower
....

=== scripts/methods/method_proc

*Source*

[source,ruby]
----
#! /usr/bin/env ruby
# frozen_string_literal: true

require "bundler/inline"

gemfile true do
source "https://rubygems.org"

gem "benchmark-ips"
end

Example = Class.new do
def initialize words
@words = words
@first_word = words.first
end

def direct_single
say first_word
end

def direct_multiple
words.each { |word| say word }
end

def proc_single
method(:say).call first_word
end

def proc_multiple
words.each { |word| method(:say).call word }
end

def method_to_proc_single
first_word.then(&method(:say))
end

def method_to_proc_multiple
words.each(&method(:say))
end

private

attr_reader :words, :first_word

def say phrase
"You said: #{phrase}."
end
end

example = Example.new %w[one two three]

Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2

benchmark.report("Direct (s)") { example.direct_single }
benchmark.report("Direct (m)") { example.direct_multiple }
benchmark.report("Proc (s)") { example.proc_single }
benchmark.report("Proc (m)") { example.proc_multiple }
benchmark.report("Method To Proc (s)") { example.method_to_proc_single }
benchmark.report("Method To Proc (m)") { example.method_to_proc_multiple }

benchmark.compare!
end
----

*Benchmark*

....
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0]
Warming up --------------------------------------
Direct (s) 1.154M i/100ms
Direct (m) 391.284k i/100ms
Proc (s) 547.383k i/100ms
Proc (m) 167.519k i/100ms
Method To Proc (s) 260.729k i/100ms
Method To Proc (m) 176.733k i/100ms
Calculating -------------------------------------
Direct (s) 12.832M (± 4.1%) i/s - 64.611M in 5.043941s
Direct (m) 4.159M (± 4.2%) i/s - 21.129M in 5.088977s
Proc (s) 6.454M (± 6.0%) i/s - 32.296M in 5.023367s
Proc (m) 1.830M (± 3.9%) i/s - 9.214M in 5.043206s
Method To Proc (s) 2.874M (± 3.4%) i/s - 14.601M in 5.086253s
Method To Proc (m) 1.962M (± 3.3%) i/s - 9.897M in 5.048962s

Comparison:
Direct (s): 12831929.9 i/s
Proc (s): 6453824.9 i/s - 1.99x slower
Direct (m): 4158707.8 i/s - 3.09x slower
Method To Proc (s): 2874279.1 i/s - 4.46x slower
Method To Proc (m): 1962181.8 i/s - 6.54x slower
Proc (m): 1829728.8 i/s - 7.01x slower
....

=== scripts/methods/send

*Source*

[source,ruby]
----
#! /usr/bin/env ruby
# frozen_string_literal: true

require "bundler/inline"

gemfile true do
source "https://rubygems.org"

gem "benchmark-ips"
end

module Static
def self.call = rand > 0.5 ? one : two

def self.one = 1

def self.two = 2
end

module Dynamic
def self.with_strings = public_send rand > 0.5 ? "one" : "two"

def self.with_symbols = public_send rand > 0.5 ? :one : :two

def self.one = 1

def self.two = 2
end

Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2
max = 1_000_000

benchmark.report("Static") { max.times { Static.call } }
benchmark.report("Dynamic (strings)") { max.times { Dynamic.with_strings } }
benchmark.report("Dynamic (symbols)") { max.times { Dynamic.with_symbols } }

benchmark.compare!
end
----

*Benchmark*

....
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0]
Warming up --------------------------------------
Static 2.000 i/100ms
Dynamic (strings) 1.000 i/100ms
Dynamic (symbols) 1.000 i/100ms
Calculating -------------------------------------
Static 27.041 (± 0.0%) i/s - 136.000 in 5.029371s
Dynamic (strings) 11.600 (± 0.0%) i/s - 59.000 in 5.086066s
Dynamic (symbols) 15.589 (± 0.0%) i/s - 78.000 in 5.003651s

Comparison:
Static: 27.0 i/s
Dynamic (symbols): 15.6 i/s - 1.73x slower
Dynamic (strings): 11.6 i/s - 2.33x slower
....

=== scripts/numerics

*Source*

[source,ruby]
----
#! /usr/bin/env ruby
# frozen_string_literal: true

require "bundler/inline"

gemfile true do
source "https://rubygems.org"

gem "bigdecimal"
gem "benchmark-ips"
end

require "bigdecimal"

Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2

benchmark.report("Integer") { 1 + 0 }
benchmark.report("Float") { 0.0001 + 0 }
benchmark.report("Rational") { (1 / 1000r) + 0 }
benchmark.report("BigDecimal") { BigDecimal("0.0001") + 0 }

benchmark.compare!
end
----

*Benchmark*

....
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0]
Warming up --------------------------------------
Integer 4.945M i/100ms
Float 3.898M i/100ms
Rational 957.175k i/100ms
BigDecimal 226.746k i/100ms
Calculating -------------------------------------
Integer 75.084M (± 4.5%) i/s - 375.824M in 5.021257s
Float 60.640M (± 0.6%) i/s - 304.039M in 5.013994s
Rational 10.254M (± 0.6%) i/s - 51.687M in 5.040700s
BigDecimal 2.332M (± 4.5%) i/s - 11.791M in 5.066520s

Comparison:
Integer: 75083686.6 i/s
Float: 60640359.1 i/s - 1.24x slower
Rational: 10254439.4 i/s - 7.32x slower
BigDecimal: 2332151.1 i/s - 32.20x slower
....

=== scripts/pattern_matching/multiple_type_check

*Source*

[source,ruby]
----
#! /usr/bin/env ruby
# frozen_string_literal: true

require "bundler/inline"

gemfile true do
source "https://rubygems.org"
gem "benchmark-ips"
end

Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2

benchmark.report("include?") { [String, Symbol].include? :test.class }
benchmark.report("in") { :test in String | Symbol }

benchmark.compare!
end
----

*Benchmark*

....
ruby 3.3.5 (2024-09-03 revision ef084cc8f4) +YJIT [arm64-darwin23.6.0]
Warming up --------------------------------------
include? 2.133M i/100ms
in 2.125M i/100ms
Calculating -------------------------------------
include? 24.603M (± 1.5%) i/s (40.65 ns/i) - 123.710M in 5.029399s
in 26.547M (± 1.7%) i/s (37.67 ns/i) - 133.855M in 5.043962s

Comparison:
in: 26546608.0 i/s
include?: 24602944.9 i/s - 1.08x slower
....

=== scripts/pattern_matching/single_type_check

*Source*

[source,ruby]
----
#! /usr/bin/env ruby
# frozen_string_literal: true

require "bundler/inline"

gemfile true do
source "https://rubygems.org"
gem "benchmark-ips"
end

Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2

benchmark.report("is_a?") { :test.is_a? Symbol }
benchmark.report("in") { :test in Symbol }

benchmark.compare!
end
----

*Benchmark*

....
ruby 3.3.5 (2024-09-03 revision ef084cc8f4) +YJIT [arm64-darwin23.6.0]
Warming up --------------------------------------
is_a? 4.544M i/100ms
in 3.022M i/100ms
Calculating -------------------------------------
is_a? 72.453M (± 0.5%) i/s (13.80 ns/i) - 363.542M in 5.017741s
in 39.524M (± 2.1%) i/s (25.30 ns/i) - 199.473M in 5.049727s

Comparison:
is_a?: 72453047.7 i/s
in: 39523743.1 i/s - 1.83x slower
....

=== scripts/refinements/import

*Source*

[source,ruby]
----
#! /usr/bin/env ruby
# frozen_string_literal: true

require "bundler/inline"

gemfile true do
source "https://rubygems.org"

gem "benchmark-ips"
end

module Import
def dud = true
end

Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2

benchmark.report "With" do
Module.new { refine(String) { import_methods Import } }
end

benchmark.report "Without" do
Module.new { def dud = true }
end

benchmark.compare!
end
----

*Benchmark*

....
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0]
Warming up --------------------------------------
With 1.438k i/100ms
Without 365.822k i/100ms
Calculating -------------------------------------
With 21.970k (±190.8%) i/s - 43.140k in 5.067653s
Without 3.636M (± 7.1%) i/s - 18.291M in 5.054830s

Comparison:
Without: 3635623.1 i/s
With: 21969.9 i/s - 165.48x slower
....

=== scripts/refinements/initialize

*Source*

[source,ruby]
----
#! /usr/bin/env ruby
# frozen_string_literal: true

require "bundler/inline"

gemfile true do
source "https://rubygems.org"

gem "benchmark-ips"
end

module Refines
refine String do
def dud = true
end
end

class With
using Refines

def initialize value = "demo"
@value = value
end
end

class Without
def initialize value = "demo"
@value = value
end
end

Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2

benchmark.report("With") { With.new }
benchmark.report("Without") { Without.new }

benchmark.compare!
end
----

*Benchmark*

....
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0]
Warming up --------------------------------------
With 1.118M i/100ms
Without 1.067M i/100ms
Calculating -------------------------------------
With 12.286M (± 5.5%) i/s - 61.467M in 5.019067s
Without 11.971M (± 5.7%) i/s - 59.775M in 5.010076s

Comparison:
With: 12285927.1 i/s
Without: 11970917.9 i/s - same-ish: difference falls within error
....

=== scripts/refinements/message

*Source*

[source,ruby]
----
#! /usr/bin/env ruby
# frozen_string_literal: true

require "bundler/inline"

gemfile true do
source "https://rubygems.org"

gem "benchmark-ips"
end

module Refines
refine String do
def dud = true
end
end

module With
using Refines

def self.call(value) = value.dud
end

module Without
def self.call(value) = value
end

value = "demo"

Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2

benchmark.report("With") { With.call value }
benchmark.report("Without") { Without.call value }

benchmark.compare!
end
----

*Benchmark*

....
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0]
Warming up --------------------------------------
With 2.681M i/100ms
Without 3.796M i/100ms
Calculating -------------------------------------
With 37.893M (± 1.2%) i/s - 190.353M in 5.024141s
Without 59.787M (± 4.2%) i/s - 299.874M in 5.024598s

Comparison:
Without: 59786895.6 i/s
With: 37893449.5 i/s - 1.58x slower
....

=== scripts/refinements/refine

*Source*

[source,ruby]
----
#! /usr/bin/env ruby
# frozen_string_literal: true

require "bundler/inline"

gemfile true do
source "https://rubygems.org"

gem "benchmark-ips"
end

Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2

benchmark.report "With" do
Module.new do
refine String do
def dud = true
end
end
end

benchmark.report "Without" do
Module.new do
def dud = true
end
end

benchmark.compare!
end
----

*Benchmark*

....
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0]
Warming up --------------------------------------
With 1.497k i/100ms
Without 337.437k i/100ms
Calculating -------------------------------------
With 21.225k (±192.4%) i/s - 35.928k in 5.093624s
Without 3.284M (± 8.5%) i/s - 16.534M in 5.067713s

Comparison:
Without: 3284418.0 i/s
With: 21224.8 i/s - 154.74x slower
....

=== scripts/strings/concatenation

*Source*

[source,ruby]
----
#! /usr/bin/env ruby
# frozen_string_literal: true

require "bundler/inline"

gemfile true do
source "https://rubygems.org"

gem "benchmark-ips"
end

one = "One"
two = "Two"
three = "Three"
four = "Four"
five = "Five"
six = "Six"
seven = "Seven"
eight = "Eight"
nine = "Nine"
ten = "Ten"

Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2

benchmark.report "Implicit (<)" do
"One" "Two"
end

benchmark.report "Implicit (>)" do
"One" "Two" "Three" "Four" "Five" "Six" "Seven" "Eight" "Nine" "Ten"
end

benchmark.report "Interpolation (<)" do
"#{one} #{two}"
end

benchmark.report "Interpolation (>)" do
"#{one} #{two} #{three} #{four} #{five} #{six} #{seven} #{eight} #{nine} #{ten}"
end

benchmark.report "#+ (<)" do
one + " " + two
end

benchmark.report "#+ (>)" do
one + " " + two + " " + three + " " + four + " " + five + " " + six + " " + seven + " " +
eight + " " + nine + " " + ten
end

# WARNING: Mutation.
benchmark.report "#concat (<)" do
one.dup.concat two
end

# WARNING: Mutation.
benchmark.report "#concat (>)" do
one.dup.concat two, three, four, five, six, seven, eight, nine, ten
end

# WARNING: Mutation.
benchmark.report "#<< (<)" do
one.dup << two
end

# WARNING: Mutation.
benchmark.report "#<< (>)" do
one.dup << two << three << four << five << six << seven << eight << nine << ten
end

benchmark.report "Array#join (<)" do
[one, two].join " "
end

benchmark.report "Array#join (>)" do
[one, two, three, four, five, six, seven, eight, nine, ten].join " "
end

benchmark.compare!
end
----

*Benchmark*

....
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0]
Warming up --------------------------------------
Implicit (<) 4.372M i/100ms
Implicit (>) 4.448M i/100ms
Interpolation (<) 1.208M i/100ms
Interpolation (>) 367.925k i/100ms
#+ (<) 823.216k i/100ms
#+ (>) 80.833k i/100ms
#concat (<) 1.369M i/100ms
#concat (>) 291.617k i/100ms
#<< (<) 1.479M i/100ms
#<< (>) 459.494k i/100ms
Array#join (<) 722.879k i/100ms
Array#join (>) 307.222k i/100ms
Calculating -------------------------------------
Implicit (<) 75.685M (± 1.7%) i/s - 380.338M in 5.026812s
Implicit (>) 75.105M (± 1.8%) i/s - 378.120M in 5.036297s
Interpolation (<) 12.722M (± 5.7%) i/s - 64.005M in 5.047960s
Interpolation (>) 3.868M (± 5.9%) i/s - 19.500M in 5.057509s
#+ (<) 8.701M (± 7.1%) i/s - 43.630M in 5.038462s
#+ (>) 837.724k (± 7.7%) i/s - 4.203M in 5.048670s
#concat (<) 14.496M (± 8.5%) i/s - 72.583M in 5.045357s
#concat (>) 2.959M (± 8.8%) i/s - 14.872M in 5.063294s
#<< (<) 15.944M (± 8.0%) i/s - 79.888M in 5.043131s
#<< (>) 4.739M (± 9.0%) i/s - 23.894M in 5.082877s
Array#join (<) 7.392M (± 7.9%) i/s - 36.867M in 5.018258s
Array#join (>) 3.275M (± 7.8%) i/s - 16.283M in 5.000238s

Comparison:
Implicit (<): 75684765.3 i/s
Implicit (>): 75104861.7 i/s - same-ish: difference falls within error
#<< (<): 15944068.0 i/s - 4.75x slower
#concat (<): 14496048.1 i/s - 5.22x slower
Interpolation (<): 12722385.9 i/s - 5.95x slower
#+ (<): 8700892.4 i/s - 8.70x slower
Array#join (<): 7392065.5 i/s - 10.24x slower
#<< (>): 4738942.8 i/s - 15.97x slower
Interpolation (>): 3867990.4 i/s - 19.57x slower
Array#join (>): 3274997.2 i/s - 23.11x slower
#concat (>): 2959275.8 i/s - 25.58x slower
#+ (>): 837724.4 i/s - 90.35x slower
....

=== scripts/strings/matching

*Source*

[source,ruby]
----
#! /usr/bin/env ruby
# frozen_string_literal: true

require "bundler/inline"

gemfile true do
source "https://rubygems.org"

gem "benchmark-ips"
end

require "securerandom"

word = SecureRandom.alphanumeric 100
string_matcher = "a"
regex_matcher = /\Aa/

Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2

benchmark.report("#match?") { word.match? regex_matcher }
benchmark.report("#=~") { word =~ regex_matcher }
benchmark.report("#start_with? (String)") { word.start_with? string_matcher }
benchmark.report("#start_with? (Regex)") { word.start_with? regex_matcher }
benchmark.report("#end_with?") { word.end_with? string_matcher }

benchmark.compare!
end
----

*Benchmark*

....
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0]
Warming up --------------------------------------
#match? 2.422M i/100ms
#=~ 742.580k i/100ms
#start_with? (String)
2.965M i/100ms
#start_with? (Regex) 679.369k i/100ms
#end_with? 3.069M i/100ms
Calculating -------------------------------------
#match? 28.797M (± 5.0%) i/s - 145.346M in 5.060238s
#=~ 7.620M (± 9.1%) i/s - 37.872M in 5.008839s
#start_with? (String)
34.312M (± 5.0%) i/s - 171.982M in 5.024675s
#start_with? (Regex) 7.464M (± 7.4%) i/s - 37.365M in 5.034025s
#end_with? 37.141M (± 0.5%) i/s - 187.219M in 5.040811s

Comparison:
#end_with?: 37141486.2 i/s
#start_with? (String): 34312493.3 i/s - 1.08x slower
#match?: 28797216.7 i/s - 1.29x slower
#=~: 7620209.0 i/s - 4.87x slower
#start_with? (Regex): 7463669.2 i/s - 4.98x slower
....

=== scripts/strings/split

*Source*

[source,ruby]
----
#! /usr/bin/env ruby
# frozen_string_literal: true

require "bundler/inline"

gemfile true do
source "https://rubygems.org"

gem "benchmark-ips"
end

require "securerandom"

words = Array.new(100_000) { SecureRandom.alphanumeric 10 }
delimiter = " "
text = words.join delimiter
pattern = /\Aa/

Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2

benchmark.report "Without Block" do
text.split(delimiter).grep(pattern)
end

benchmark.report "With Block" do
selections = []
text.split(delimiter) { |word| selections << word if word.match? pattern }
end

benchmark.compare!
end
----

*Benchmark*

....
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0]
Warming up --------------------------------------
Without Block 13.000 i/100ms
With Block 13.000 i/100ms
Calculating -------------------------------------
Without Block 136.799 (± 2.2%) i/s - 689.000 in 5.039420s
With Block 134.538 (± 2.2%) i/s - 676.000 in 5.026808s

Comparison:
Without Block: 136.8 i/s
With Block: 134.5 i/s - same-ish: difference falls within error
....

=== scripts/strings/substrings

*Source*

[source,ruby]
----
#! /usr/bin/env ruby
# frozen_string_literal: true

require "bundler/inline"

gemfile true do
source "https://rubygems.org"

gem "benchmark-ips"
end

example = "example"

Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2

benchmark.report("#sub (string)") { example.sub "x", "b" }
benchmark.report("#sub (regex)") { example.sub(/x/, "b") }
benchmark.report("#gsub (string)") { example.gsub "x", "b" }
benchmark.report("#gsub (regex)") { example.gsub(/x/, "b") }
benchmark.report("#tr") { example.tr "x", "b" }

benchmark.compare!
end
----

*Benchmark*

....
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0]
Warming up --------------------------------------
#sub (string) 484.505k i/100ms
#sub (regex) 344.692k i/100ms
#gsub (string) 414.555k i/100ms
#gsub (regex) 170.417k i/100ms
#tr 1.007M i/100ms
Calculating -------------------------------------
#sub (string) 5.449M (± 3.0%) i/s - 27.617M in 5.073218s
#sub (regex) 4.021M (± 6.1%) i/s - 20.337M in 5.074883s
#gsub (string) 4.463M (± 3.4%) i/s - 22.386M in 5.021307s
#gsub (regex) 1.710M (± 8.6%) i/s - 8.521M in 5.018878s
#tr 10.693M (± 2.8%) i/s - 54.352M in 5.087263s

Comparison:
#tr: 10692642.6 i/s
#sub (string): 5448640.0 i/s - 1.96x slower
#gsub (string): 4463049.2 i/s - 2.40x slower
#sub (regex): 4020798.1 i/s - 2.66x slower
#gsub (regex): 1710379.3 i/s - 6.25x slower
....

=== scripts/thens

*Source*

[source,ruby]
----
#! /usr/bin/env ruby
# frozen_string_literal: true

require "bundler/inline"

gemfile true do
source "https://rubygems.org"

gem "benchmark-ips"
end

Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2

benchmark.report "standard" do
one, two = "one two".split
"#{one} + #{two} = #{one + two}"
end

benchmark.report "then" do
"one two".split.then { |one, two| "#{one} + #{two} = #{one + two}" }
end

benchmark.compare!
end
----

*Benchmark*

....
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0]
Warming up --------------------------------------
standard 361.533k i/100ms
then 340.568k i/100ms
Calculating -------------------------------------
standard 3.566M (± 7.3%) i/s - 18.077M in 5.096489s
then 3.366M (± 7.6%) i/s - 17.028M in 5.088830s

Comparison:
standard: 3566064.8 i/s
then: 3365922.3 i/s - same-ish: difference falls within error
....

=== scripts/values/inheritance

*Source*

[source,ruby]
----
#! /usr/bin/env ruby
# frozen_string_literal: true

require "bundler/inline"

gemfile true do
source "https://rubygems.org"

gem "benchmark-ips"
end

PlotStruct = Struct.new :x, :y

class PlotSubclass < Struct.new :x, :y
end

struct = -> { PlotStruct[x: 1, y: 2] }
subclass = -> { PlotSubclass[x: 1, y: 2] }

Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2

benchmark.report("Struct") { struct.call }
benchmark.report("Subclass") { subclass.call }

benchmark.compare!
end
----

*Benchmark*

....
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0]
Warming up --------------------------------------
Struct 446.439k i/100ms
Subclass 438.472k i/100ms
Calculating -------------------------------------
Struct 4.925M (± 7.6%) i/s - 24.554M in 5.013174s
Subclass 4.736M (± 7.8%) i/s - 23.677M in 5.027862s

Comparison:
Struct: 4924540.0 i/s
Subclass: 4736109.1 i/s - same-ish: difference falls within error
....

=== scripts/values/initialization

*Source*

[source,ruby]
----
#! /usr/bin/env ruby
# frozen_string_literal: true

require "bundler/inline"

gemfile true do
source "https://rubygems.org"

gem "benchmark-ips"
gem "dry-struct"
end

Warning[:performance] = false

require "ostruct"

DataDefault = Data.define :a, :b, :c, :d, :e

DataCustom = Data.define :a, :b, :c, :d, :e do
def initialize a: 1, b: 2, c: 3, d: 4, e: 5
super
end
end

StructDefault = Struct.new :a, :b, :c, :d, :e

StructCustom = Struct.new :a, :b, :c, :d, :e do
def initialize a: 1, b: 2, c: 3, d: 4, e: 5
super
end
end

module Types
include Dry.Types
end

DryExample = Class.new Dry::Struct do
attribute :a, Types::Strict::Integer
attribute :b, Types::Strict::Integer
attribute :c, Types::Strict::Integer
attribute :d, Types::Strict::Integer
attribute :e, Types::Strict::Integer
end

Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2

benchmark.report("Data (positional)") { DataDefault[1, 2, 3, 4, 5] }
benchmark.report("Data (keyword)") { DataDefault[a: 1, b: 2, c: 3, d: 4, e: 5] }
benchmark.report("Data (custom)") { DataCustom.new }
benchmark.report("Struct (positional)") { StructDefault[1, 2, 3, 4, 5] }
benchmark.report("Struct (keyword)") { StructDefault[a: 1, b: 2, c: 3, d: 4, e: 5] }
benchmark.report("Struct (custom)") { StructCustom.new }
benchmark.report("OpenStruct") { OpenStruct.new a: 1, b: 2, c: 3, d: 4, e: 5 }
benchmark.report("Dry Struct") { DryExample[a: 1, b: 2, c: 3, d: 4, e: 5] }

benchmark.compare!
end
----

*Benchmark*

....
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0]
Warming up --------------------------------------
Data (positional) 304.848k i/100ms
Data (keyword) 317.019k i/100ms
Data (custom) 224.669k i/100ms
Struct (positional) 722.167k i/100ms
Struct (keyword) 302.062k i/100ms
Struct (custom) 292.850k i/100ms
OpenStruct 743.000 i/100ms
Dry Struct 111.304k i/100ms
Calculating -------------------------------------
Data (positional) 3.120M (± 9.7%) i/s - 15.547M in 5.025792s
Data (keyword) 3.191M (± 9.7%) i/s - 15.851M in 5.008582s
Data (custom) 2.226M (± 3.6%) i/s - 11.233M in 5.054091s
Struct (positional) 6.869M (±10.4%) i/s - 34.664M in 5.098769s
Struct (keyword) 3.059M (± 9.6%) i/s - 15.405M in 5.077727s
Struct (custom) 2.945M (±11.4%) i/s - 14.642M in 5.031281s
OpenStruct 1.856k (±24.0%) i/s - 9.659k in 5.485483s
Dry Struct 1.123M (±11.8%) i/s - 5.565M in 5.029750s

Comparison:
Struct (positional): 6868838.9 i/s
Data (keyword): 3191343.7 i/s - 2.15x slower
Data (positional): 3120155.8 i/s - 2.20x slower
Struct (keyword): 3059412.6 i/s - 2.25x slower
Struct (custom): 2945238.4 i/s - 2.33x slower
Data (custom): 2225751.7 i/s - 3.09x slower
Dry Struct: 1123063.3 i/s - 6.12x slower
OpenStruct: 1855.9 i/s - 3701.01x slower

ℹ️ `Data` is fastest when members are small (like three or less) but performance degrades when more members are added (like five or more). This is because `Data` always initializes with a `Hash` which is not the case with a `Struct`. Additionally, passing keyword arguments to/from Ruby to Ruby is optimized while to/from Ruby/C is not.
....

=== scripts/values/reading

*Source*

[source,ruby]
----
#! /usr/bin/env ruby
# frozen_string_literal: true

require "bundler/inline"

gemfile true do
source "https://rubygems.org"

gem "benchmark-ips"
gem "dry-struct"
end

require "ostruct"

DataExample = Data.define :to, :from
StructExample = Struct.new :to, :from

module Types
include Dry.Types
end

DryExample = Class.new Dry::Struct do
attribute :to, Types::Strict::String
attribute :from, Types::Strict::String
end

data = DataExample[to: "Rick", from: "Morty"]
struct = StructExample[to: "Rick", from: "Morty"]
open_struct = OpenStruct.new to: "Rick", from: "Morty"
dry_struct = DryExample[to: "Rick", from: "Morty"]

Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2

benchmark.report("Data") { data.to }
benchmark.report("Struct") { struct.to }
benchmark.report("OpenStruct") { open_struct.to }
benchmark.report("Dry Struct") { dry_struct.to }

benchmark.compare!
end
----

*Benchmark*

....
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0]
Warming up --------------------------------------
Data 4.313M i/100ms
Struct 3.960M i/100ms
OpenStruct 3.419M i/100ms
Dry Struct 3.646M i/100ms
Calculating -------------------------------------
Data 72.832M (± 0.1%) i/s - 366.637M in 5.034027s
Struct 68.730M (± 0.4%) i/s - 344.554M in 5.013242s
OpenStruct 49.555M (± 0.1%) i/s - 249.580M in 5.036400s
Dry Struct 49.635M (± 0.1%) i/s - 251.578M in 5.068604s

Comparison:
Data: 72831756.3 i/s
Struct: 68729971.7 i/s - 1.06x slower
Dry Struct: 49634683.0 i/s - 1.47x slower
OpenStruct: 49555343.3 i/s - 1.47x slower
....

=== scripts/values/writing

*Source*

[source,ruby]
----
#! /usr/bin/env ruby
# frozen_string_literal: true

require "bundler/inline"

gemfile true do
source "https://rubygems.org"

gem "benchmark-ips"
end

require "ostruct"

DataExample = Data.define :to, :from
StructExample = Struct.new :to, :from

data = DataExample[to: "Rick", from: "Morty"]
struct = StructExample[to: "Rick", from: "Morty"]
open_struct = OpenStruct.new to: "Rick", from: "Morty"

Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2

benchmark.report("Data") { data.with from: "Summer" }
benchmark.report("Struct") { struct.from = "Summer" }
benchmark.report("OpenStruct") { open_struct.from = "Summer" }

benchmark.compare!
end
----

*Benchmark*

....
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0]
Warming up --------------------------------------
Data 265.287k i/100ms
Struct 4.274M i/100ms
OpenStruct 2.870M i/100ms
Calculating -------------------------------------
Data 2.721M (± 8.0%) i/s - 13.530M in 5.002940s
Struct 55.482M (± 1.5%) i/s - 277.793M in 5.008024s
OpenStruct 34.810M (± 0.2%) i/s - 175.090M in 5.029864s

Comparison:
Struct: 55482353.9 i/s
OpenStruct: 34810159.3 i/s - 1.59x slower
Data: 2721426.3 i/s - 20.39x slower
....

== Development

To contribute, run:

[source,bash]
----
git clone https://github.com/bkuhlmann/benchmarks.git
cd benchmarks
bin/setup
----

To render documentation for all benchmark scripts, run:

[source,bash]
----
bin/render
----

This is the same script used to update the documentation within this README.

== Tests

To test, run:

[source,bash]
----
bin/rake
----

== link:https://alchemists.io/policies/license[License]

== link:https://alchemists.io/policies/security[Security]

== link:https://alchemists.io/policies/code_of_conduct[Code of Conduct]

== link:https://alchemists.io/policies/contributions[Contributions]

== link:https://alchemists.io/policies/developer_certificate_of_origin[Developer Certificate of Origin]

== link:https://alchemists.io/projects/benchmarks/versions[Versions]

== link:https://alchemists.io/community[Community]

== Credits

* Built with link:https://alchemists.io/projects/rubysmith[Rubysmith].
* Engineered by link:https://alchemists.io/team/brooke_kuhlmann[Brooke Kuhlmann].