benchmarks

A collection of micro benchmarks.

https://github.com/bkuhlmann/benchmarks

Science Score: 44.0%

This score indicates how likely this project is to be science-related based on various indicators:

  • CITATION.cff file
    Found CITATION.cff file
  • codemeta.json file
    Found codemeta.json file
  • .zenodo.json file
    Found .zenodo.json file
  • DOI references
  • Academic publication links
  • Academic email domains
  • Institutional organization owner
  • JOSS paper metadata
  • Scientific vocabulary similarity
    Low similarity (8.5%) to scientific vocabulary

Keywords

benchmark micro speed
Last synced: 6 months ago · JSON representation ·

Repository

A collection of micro benchmarks.

Basic Info
Statistics
  • Stars: 6
  • Watchers: 2
  • Forks: 0
  • Open Issues: 0
  • Releases: 0
Topics
benchmark micro speed
Created about 7 years ago · Last pushed 6 months ago
Metadata Files
Readme Funding License Citation

README.adoc

: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 5.4.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*
[%collapsible]
====
[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.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin24.2.0]
Warming up --------------------------------------
                  #+     2.130M i/100ms
                 #+=   927.861k i/100ms
             #concat   691.057k i/100ms
                  #|   483.602k i/100ms
      #<< + #flatten   203.055k i/100ms
    splat + #flatten   212.664k i/100ms
         multi-splat   903.880k i/100ms
Calculating -------------------------------------
                  #+     23.159M (± 0.7%) i/s   (43.18 ns/i) -    117.130M in   5.057953s
                 #+=      9.970M (± 0.8%) i/s  (100.30 ns/i) -     50.104M in   5.025637s
             #concat      7.333M (± 1.6%) i/s  (136.37 ns/i) -     37.317M in   5.090383s
                  #|      4.824M (± 1.8%) i/s  (207.30 ns/i) -     24.180M in   5.014471s
      #<< + #flatten      2.101M (± 1.1%) i/s  (475.99 ns/i) -     10.559M in   5.026496s
    splat + #flatten      2.156M (± 1.2%) i/s  (463.80 ns/i) -     10.846M in   5.031006s
         multi-splat      9.786M (± 2.0%) i/s  (102.19 ns/i) -     49.713M in   5.082000s

Comparison:
                  #+: 23158921.5 i/s
                 #+=:  9970346.4 i/s - 2.32x  slower
         multi-splat:  9786149.7 i/s - 2.37x  slower
             #concat:  7332786.6 i/s - 3.16x  slower
                  #|:  4823810.9 i/s - 4.80x  slower
    splat + #flatten:  2156124.7 i/s - 10.74x  slower
      #<< + #flatten:  2100889.3 i/s - 11.02x  slower
....

=== scripts/arrays/search

.*Source*
[%collapsible]
====
[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.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin24.2.0]
Warming up --------------------------------------
               #grep   198.448k i/100ms
             #select   237.432k i/100ms
Calculating -------------------------------------
               #grep      1.997M (± 1.3%) i/s  (500.69 ns/i) -     10.121M in   5.068227s
             #select      2.468M (± 0.9%) i/s  (405.26 ns/i) -     12.346M in   5.003928s

Comparison:
             #select:  2467557.1 i/s
               #grep:  1997260.3 i/s - 1.24x  slower
....

=== scripts/bindings

.*Source*
[%collapsible]
====
[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.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin24.2.0]
Warming up --------------------------------------
             Binding   685.023k i/100ms
             Pinning     1.868M i/100ms
Calculating -------------------------------------
             Binding      7.416M (± 2.1%) i/s  (134.85 ns/i) -     37.676M in   5.082785s
             Pinning     20.890M (± 2.7%) i/s   (47.87 ns/i) -    104.605M in   5.011139s

Comparison:
             Pinning: 20890279.6 i/s
             Binding:  7415714.9 i/s - 2.82x  slower
....

=== scripts/constants/lookup

.*Source*
[%collapsible]
====
[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.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin24.2.0]
Warming up --------------------------------------
                 #[]     2.897M i/100ms
 Module.get (symbol)     3.406M i/100ms
 Module.get (string)     1.669M i/100ms
          Object.get     1.011M i/100ms
Calculating -------------------------------------
                 #[]     33.548M (± 0.9%) i/s   (29.81 ns/i) -    168.050M in   5.009641s
 Module.get (symbol)     42.820M (± 0.1%) i/s   (23.35 ns/i) -    214.596M in   5.011591s
 Module.get (string)     18.319M (± 0.3%) i/s   (54.59 ns/i) -     91.822M in   5.012443s
          Object.get     11.053M (± 0.2%) i/s   (90.48 ns/i) -     55.582M in   5.028808s

Comparison:
 Module.get (symbol): 42820077.0 i/s
                 #[]: 33548240.7 i/s - 1.28x  slower
 Module.get (string): 18319033.6 i/s - 2.34x  slower
          Object.get: 11052680.4 i/s - 3.87x  slower
....

=== scripts/delegates

.*Source*
[%collapsible]
====
[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.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin24.2.0]
Warming up --------------------------------------
             Forward     2.172M i/100ms
            Delegate     2.005M i/100ms
    Simple Delegator   494.121k i/100ms
      Delegate Class   486.884k i/100ms
Calculating -------------------------------------
             Forward     26.423M (± 0.2%) i/s   (37.85 ns/i) -    132.481M in   5.013922s
            Delegate     23.553M (± 0.5%) i/s   (42.46 ns/i) -    118.295M in   5.022589s
    Simple Delegator      5.399M (± 0.9%) i/s  (185.23 ns/i) -     27.177M in   5.034498s
      Delegate Class      5.404M (± 0.6%) i/s  (185.04 ns/i) -     27.266M in   5.045399s

Comparison:
             Forward: 26422769.4 i/s
            Delegate: 23553279.8 i/s - 1.12x  slower
      Delegate Class:  5404232.5 i/s - 4.89x  slower
    Simple Delegator:  5398560.6 i/s - 4.89x  slower
....

=== scripts/functions/blocks

.*Source*
[%collapsible]
====
[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.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin24.2.0]
Warming up --------------------------------------
    Block (implicit)     4.215M i/100ms
Block (implicit guard)
                         3.820M i/100ms
    Block (explicit)   787.233k i/100ms
Block (explicit guard)
                       783.153k i/100ms
              Lambda     2.806M i/100ms
                Proc     2.983M i/100ms
Calculating -------------------------------------
    Block (implicit)     59.814M (± 0.2%) i/s   (16.72 ns/i) -    299.269M in   5.003373s
Block (implicit guard)
                         56.952M (± 0.4%) i/s   (17.56 ns/i) -    286.516M in   5.030941s
    Block (explicit)      8.892M (± 1.0%) i/s  (112.46 ns/i) -     44.872M in   5.046771s
Block (explicit guard)
                          8.804M (± 1.0%) i/s  (113.59 ns/i) -     44.640M in   5.070946s
              Lambda     34.991M (± 0.9%) i/s   (28.58 ns/i) -    176.772M in   5.052370s
                Proc     35.366M (± 0.9%) i/s   (28.28 ns/i) -    178.993M in   5.061491s

Comparison:
    Block (implicit): 59813544.8 i/s
Block (implicit guard): 56951704.4 i/s - 1.05x  slower
                Proc: 35366437.5 i/s - 1.69x  slower
              Lambda: 34990583.2 i/s - 1.71x  slower
    Block (explicit):  8892108.2 i/s - 6.73x  slower
Block (explicit guard):  8803962.5 i/s - 6.79x  slower
....

=== scripts/functions/static

.*Source*
[%collapsible]
====
[source,ruby]
----
#! /usr/bin/env ruby
# frozen_string_literal: true

require "bundler/inline"

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

  gem "benchmark-ips"
end

class_example = Class.new do
  def self.call(first, second) = first + second
end

module_example = Module.new do
  module_function

  def call(first, second) = first + second
end

function_example = -> first, second { first + second }

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

  benchmark.report("Class") { class_example.call 10, 20 }
  benchmark.report("Module") { module_example.call 10, 20 }
  benchmark.report("Function") { function_example.call 10, 20 }

  benchmark.compare!
end
----
====

*Benchmark*

....
ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin24.2.0]
Warming up --------------------------------------
               Class     4.586M i/100ms
              Module     4.072M i/100ms
            Function     2.714M i/100ms
Calculating -------------------------------------
               Class     62.525M (± 0.4%) i/s   (15.99 ns/i) -    316.450M in   5.061239s
              Module     62.654M (± 0.2%) i/s   (15.96 ns/i) -    313.576M in   5.004938s
            Function     31.392M (± 2.1%) i/s   (31.86 ns/i) -    157.390M in   5.015900s

Comparison:
              Module: 62653616.0 i/s
               Class: 62525065.5 i/s - same-ish: difference falls within error
            Function: 31391523.8 i/s - 2.00x  slower
....

=== scripts/hashes/lookup

.*Source*
[%collapsible]
====
[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.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin24.2.0]
Warming up --------------------------------------
                 #[]     4.108M i/100ms
              #fetch     3.613M i/100ms
    #fetch (default)     3.746M i/100ms
      #fetch (block)     3.709M i/100ms
                #dig     4.047M i/100ms
Calculating -------------------------------------
                 #[]     51.006M (± 0.3%) i/s   (19.61 ns/i) -    258.811M in   5.074111s
              #fetch     44.278M (± 0.6%) i/s   (22.58 ns/i) -    224.011M in   5.059357s
    #fetch (default)     44.317M (± 0.3%) i/s   (22.56 ns/i) -    224.757M in   5.071557s
      #fetch (block)     43.807M (± 0.4%) i/s   (22.83 ns/i) -    222.555M in   5.080432s
                #dig     48.337M (± 0.1%) i/s   (20.69 ns/i) -    242.803M in   5.023111s

Comparison:
                 #[]: 51006430.3 i/s
                #dig: 48337145.4 i/s - 1.06x  slower
    #fetch (default): 44317430.9 i/s - 1.15x  slower
              #fetch: 44278312.1 i/s - 1.15x  slower
      #fetch (block): 43807023.1 i/s - 1.16x  slower
....

=== scripts/hashes/merge

.*Source*
[%collapsible]
====
[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.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin24.2.0]
Warming up --------------------------------------
               Splat     1.220M i/100ms
               Merge   972.824k i/100ms
              Merge!     1.398M i/100ms
          Dup Merge!   728.688k i/100ms
Calculating -------------------------------------
               Splat     14.035M (± 1.5%) i/s   (71.25 ns/i) -     70.757M in   5.042521s
               Merge     10.595M (± 1.4%) i/s   (94.38 ns/i) -     53.505M in   5.050960s
              Merge!     14.980M (± 1.2%) i/s   (66.76 ns/i) -     75.471M in   5.038965s
          Dup Merge!      7.787M (± 1.1%) i/s  (128.41 ns/i) -     39.349M in   5.053630s

Comparison:
              Merge!: 14979505.8 i/s
               Splat: 14035287.2 i/s - 1.07x  slower
               Merge: 10595050.3 i/s - 1.41x  slower
          Dup Merge!:  7787275.3 i/s - 1.92x  slower
....

=== scripts/hashes/reduce

.*Source*
[%collapsible]
====
[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.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin24.2.0]
Warming up --------------------------------------
              Reduce    64.415k i/100ms
         With Object   100.991k i/100ms
Calculating -------------------------------------
              Reduce    656.529k (± 1.0%) i/s    (1.52 μs/i) -      3.285M in   5.004376s
         With Object      1.032M (± 0.8%) i/s  (969.23 ns/i) -      5.252M in   5.090269s

Comparison:
         With Object:  1031743.5 i/s
              Reduce:   656529.5 i/s - 1.57x  slower
....

=== scripts/loops

.*Source*
[%collapsible]
====
[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.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin24.2.0]
Warming up --------------------------------------
                 for    19.313k i/100ms
               #each    21.611k i/100ms
Calculating -------------------------------------
                 for    193.383k (± 0.1%) i/s    (5.17 μs/i) -    984.963k in   5.093344s
               #each    215.690k (± 0.1%) i/s    (4.64 μs/i) -      1.081M in   5.009754s

Comparison:
               #each:   215689.6 i/s
                 for:   193382.6 i/s - 1.12x  slower
....

=== scripts/methods/define_method

.*Source*
[%collapsible]
====
[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.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin24.2.0]
Warming up --------------------------------------
             Wrapped     1.582M i/100ms
             Defined     1.639M i/100ms
           Delegated     1.150M i/100ms
Calculating -------------------------------------
             Wrapped     17.189M (± 1.0%) i/s   (58.18 ns/i) -     87.037M in   5.064110s
             Defined     17.717M (± 1.0%) i/s   (56.44 ns/i) -     90.126M in   5.087374s
           Delegated     12.469M (± 0.5%) i/s   (80.20 ns/i) -     63.277M in   5.074680s

Comparison:
             Defined: 17717413.2 i/s
             Wrapped: 17188761.0 i/s - 1.03x  slower
           Delegated: 12469384.0 i/s - 1.42x  slower
....

=== scripts/methods/method_proc

.*Source*
[%collapsible]
====
[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.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin24.2.0]
Warming up --------------------------------------
          Direct (s)     1.686M i/100ms
          Direct (m)   581.545k i/100ms
            Proc (s)   771.008k i/100ms
            Proc (m)   277.976k i/100ms
  Method To Proc (s)   351.620k i/100ms
  Method To Proc (m)   222.732k i/100ms
Calculating -------------------------------------
          Direct (s)     18.494M (± 1.0%) i/s   (54.07 ns/i) -     92.704M in   5.013031s
          Direct (m)      6.220M (± 1.4%) i/s  (160.76 ns/i) -     31.403M in   5.049559s
            Proc (s)      8.887M (± 0.9%) i/s  (112.53 ns/i) -     44.718M in   5.032556s
            Proc (m)      2.913M (± 1.0%) i/s  (343.32 ns/i) -     14.733M in   5.058585s
  Method To Proc (s)      3.771M (± 1.5%) i/s  (265.18 ns/i) -     18.987M in   5.036310s
  Method To Proc (m)      2.298M (± 0.9%) i/s  (435.17 ns/i) -     11.582M in   5.040560s

Comparison:
          Direct (s): 18494466.4 i/s
            Proc (s):  8886651.1 i/s - 2.08x  slower
          Direct (m):  6220285.8 i/s - 2.97x  slower
  Method To Proc (s):  3770971.6 i/s - 4.90x  slower
            Proc (m):  2912720.9 i/s - 6.35x  slower
  Method To Proc (m):  2297978.5 i/s - 8.05x  slower
....

=== scripts/methods/scoped_send

.*Source*
[%collapsible]
====
[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 Example
  module_function

  def add(first = 1, second = 2) = first + second
end

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

  benchmark.report("Public") { Example.public_send :add }
  benchmark.report("Private") { Example.__send__ :add }

  benchmark.compare!
end
----
====

*Benchmark*

....
ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin24.2.0]
Warming up --------------------------------------
              Public     2.011M i/100ms
             Private     4.220M i/100ms
Calculating -------------------------------------
              Public     23.979M (± 1.1%) i/s   (41.70 ns/i) -    120.645M in   5.031991s
             Private     61.137M (± 0.4%) i/s   (16.36 ns/i) -    308.063M in   5.038936s

Comparison:
             Private: 61137450.6 i/s
              Public: 23978548.9 i/s - 2.55x  slower
....

=== scripts/methods/send

.*Source*
[%collapsible]
====
[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

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

  benchmark.compare!
end
----
====

*Benchmark*

....
ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin24.2.0]
Warming up --------------------------------------
              Static     2.126M i/100ms
   Dynamic (strings)   973.273k i/100ms
   Dynamic (symbols)     1.256M i/100ms
Calculating -------------------------------------
              Static     26.137M (± 5.6%) i/s   (38.26 ns/i) -    131.819M in   5.058540s
   Dynamic (strings)     11.631M (± 0.9%) i/s   (85.98 ns/i) -     58.396M in   5.021134s
   Dynamic (symbols)     16.000M (± 1.7%) i/s   (62.50 ns/i) -     80.395M in   5.026229s

Comparison:
              Static: 26137140.5 i/s
   Dynamic (symbols): 16000110.8 i/s - 1.63x  slower
   Dynamic (strings): 11631159.4 i/s - 2.25x  slower
....

=== scripts/numerics

.*Source*
[%collapsible]
====
[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.1 + 0 }
  benchmark.report("Rational") { (1 / 1r) + 0 }
  benchmark.report("BigDecimal") { BigDecimal("0.1") + 0 }

  benchmark.compare!
end
----
====

*Benchmark*

....
ruby 3.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin24.2.0]
Warming up --------------------------------------
             Integer     4.818M i/100ms
               Float     4.688M i/100ms
            Rational     1.440M i/100ms
          BigDecimal   285.130k i/100ms
Calculating -------------------------------------
             Integer     72.057M (± 2.9%) i/s   (13.88 ns/i) -    361.327M in   5.019619s
               Float     62.474M (± 0.7%) i/s   (16.01 ns/i) -    314.066M in   5.027353s
            Rational     15.063M (± 0.3%) i/s   (66.39 ns/i) -     76.338M in   5.067855s
          BigDecimal      2.871M (± 0.9%) i/s  (348.31 ns/i) -     14.542M in   5.065332s

Comparison:
             Integer: 72057483.7 i/s
               Float: 62474330.5 i/s - 1.15x  slower
            Rational: 15063323.3 i/s - 4.78x  slower
          BigDecimal:  2871025.0 i/s - 25.10x  slower
....

=== scripts/pattern_matching/multiple_type_check

.*Source*
[%collapsible]
====
[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.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin24.2.0]
Warming up --------------------------------------
            include?     3.306M i/100ms
                  in     2.232M i/100ms
Calculating -------------------------------------
            include?     39.476M (± 0.2%) i/s   (25.33 ns/i) -    198.332M in   5.024176s
                  in     26.164M (± 0.1%) i/s   (38.22 ns/i) -    131.686M in   5.033138s

Comparison:
            include?: 39475647.2 i/s
                  in: 26163792.0 i/s - 1.51x  slower
....

=== scripts/pattern_matching/single_type_check

.*Source*
[%collapsible]
====
[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.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin24.2.0]
Warming up --------------------------------------
               is_a?     4.392M i/100ms
                  in     3.021M i/100ms
Calculating -------------------------------------
               is_a?     71.209M (± 0.2%) i/s   (14.04 ns/i) -    360.148M in   5.057619s
                  in     38.621M (± 1.2%) i/s   (25.89 ns/i) -    193.351M in   5.007114s

Comparison:
               is_a?: 71209235.1 i/s
                  in: 38621042.8 i/s - 1.84x  slower
....

=== scripts/refinements/import

.*Source*
[%collapsible]
====
[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.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin24.2.0]
Warming up --------------------------------------
                With    48.000 i/100ms
             Without   409.598k i/100ms
Calculating -------------------------------------
                With    282.342 (± 0.4%) i/s    (3.54 ms/i) -      1.440k in   5.100310s
             Without      4.139M (± 1.7%) i/s  (241.60 ns/i) -     20.889M in   5.048344s

Comparison:
             Without:  4139064.0 i/s
                With:      282.3 i/s - 14659.74x  slower
....

=== scripts/refinements/initialize

.*Source*
[%collapsible]
====
[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.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin24.2.0]
Warming up --------------------------------------
                With     1.462M i/100ms
             Without     1.463M i/100ms
Calculating -------------------------------------
                With     16.904M (± 0.9%) i/s   (59.16 ns/i) -     84.773M in   5.015288s
             Without     16.739M (± 1.1%) i/s   (59.74 ns/i) -     84.878M in   5.071267s

Comparison:
                With: 16904250.5 i/s
             Without: 16739147.2 i/s - same-ish: difference falls within error
....

=== scripts/refinements/message

.*Source*
[%collapsible]
====
[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.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin24.2.0]
Warming up --------------------------------------
                With     2.722M i/100ms
             Without     4.843M i/100ms
Calculating -------------------------------------
                With     37.130M (± 0.3%) i/s   (26.93 ns/i) -    187.803M in   5.058008s
             Without     68.976M (± 4.2%) i/s   (14.50 ns/i) -    348.707M in   5.067107s

Comparison:
             Without: 68975872.3 i/s
                With: 37130284.9 i/s - 1.86x  slower
....

=== scripts/refinements/refine

.*Source*
[%collapsible]
====
[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.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin24.2.0]
Warming up --------------------------------------
                With    52.000 i/100ms
             Without   402.935k i/100ms
Calculating -------------------------------------
                With    284.492 (± 0.7%) i/s    (3.52 ms/i) -      1.456k in   5.118210s
             Without      4.116M (± 1.8%) i/s  (242.94 ns/i) -     20.953M in   5.091996s

Comparison:
             Without:  4116202.1 i/s
                With:      284.5 i/s - 14468.62x  slower
....

=== scripts/strings/concatenation

.*Source*
[%collapsible]
====
[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

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

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

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

  # 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.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin24.2.0]
Warming up --------------------------------------
        Implicit (<)     4.799M i/100ms
        Implicit (>)     4.553M i/100ms
   Interpolation (<)     1.758M i/100ms
   Interpolation (>)   453.241k i/100ms
              #+ (<)     1.306M i/100ms
              #+ (>)   166.101k i/100ms
         #concat (<)   523.312k i/100ms
         #concat (>)   234.069k i/100ms
             #<< (<)   556.835k i/100ms
             #<< (>)   302.248k i/100ms
      Array#join (<)     1.065M i/100ms
      Array#join (>)   384.393k i/100ms
Calculating -------------------------------------
        Implicit (<)     73.295M (± 0.1%) i/s   (13.64 ns/i) -    369.501M in   5.041267s
        Implicit (>)     73.267M (± 0.3%) i/s   (13.65 ns/i) -    368.762M in   5.033156s
   Interpolation (<)     18.250M (± 2.0%) i/s   (54.79 ns/i) -     91.404M in   5.010379s
   Interpolation (>)      4.844M (± 0.9%) i/s  (206.44 ns/i) -     24.475M in   5.053113s
              #+ (<)     13.587M (± 1.9%) i/s   (73.60 ns/i) -     67.921M in   5.000883s
              #+ (>)      1.544M (± 2.3%) i/s  (647.82 ns/i) -      7.807M in   5.060098s
         #concat (<)      5.635M (± 3.1%) i/s  (177.45 ns/i) -     28.259M in   5.019545s
         #concat (>)      2.375M (± 2.7%) i/s  (421.10 ns/i) -     11.938M in   5.030584s
             #<< (<)      5.943M (± 3.4%) i/s  (168.27 ns/i) -     30.069M in   5.065748s
             #<< (>)      3.099M (± 2.8%) i/s  (322.66 ns/i) -     15.717M in   5.075307s
      Array#join (<)     11.232M (± 0.9%) i/s   (89.03 ns/i) -     56.421M in   5.023741s
      Array#join (>)      4.172M (± 1.6%) i/s  (239.70 ns/i) -     21.142M in   5.068889s

Comparison:
        Implicit (<): 73295358.8 i/s
        Implicit (>): 73267016.7 i/s - same-ish: difference falls within error
   Interpolation (<): 18250066.2 i/s - 4.02x  slower
              #+ (<): 13586594.1 i/s - 5.39x  slower
      Array#join (<): 11231833.8 i/s - 6.53x  slower
             #<< (<):  5942722.8 i/s - 12.33x  slower
         #concat (<):  5635299.8 i/s - 13.01x  slower
   Interpolation (>):  4843906.9 i/s - 15.13x  slower
      Array#join (>):  4171886.7 i/s - 17.57x  slower
             #<< (>):  3099282.3 i/s - 23.65x  slower
         #concat (>):  2374759.3 i/s - 30.86x  slower
              #+ (>):  1543630.9 i/s - 47.48x  slower
....

=== scripts/strings/matching

.*Source*
[%collapsible]
====
[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.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin24.2.0]
Warming up --------------------------------------
             #match?     2.495M i/100ms
                 #=~     1.446M i/100ms
#start_with? (String)
                         3.037M i/100ms
#start_with? (Regex)   806.851k i/100ms
          #end_with?     3.167M i/100ms
Calculating -------------------------------------
             #match?     29.221M (± 0.1%) i/s   (34.22 ns/i) -    147.203M in   5.037510s
                 #=~     15.667M (± 7.1%) i/s   (63.83 ns/i) -     78.073M in   5.023256s
#start_with? (String)
                         35.530M (± 0.4%) i/s   (28.14 ns/i) -    179.183M in   5.043172s
#start_with? (Regex)      7.438M (±24.5%) i/s  (134.44 ns/i) -     34.695M in   5.023176s
          #end_with?     36.134M (± 0.2%) i/s   (27.68 ns/i) -    183.713M in   5.084305s

Comparison:
          #end_with?: 36133533.7 i/s
#start_with? (String): 35530349.2 i/s - 1.02x  slower
             #match?: 29221455.8 i/s - 1.24x  slower
                 #=~: 15666854.2 i/s - 2.31x  slower
#start_with? (Regex):  7438366.7 i/s - 4.86x  slower
....

=== scripts/strings/split

.*Source*
[%collapsible]
====
[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.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin24.2.0]
Warming up --------------------------------------
       Without Block    16.000 i/100ms
          With Block    14.000 i/100ms
Calculating -------------------------------------
       Without Block    160.934 (± 1.9%) i/s    (6.21 ms/i) -    816.000 in   5.071684s
          With Block    147.259 (± 0.7%) i/s    (6.79 ms/i) -    742.000 in   5.039222s

Comparison:
       Without Block:      160.9 i/s
          With Block:      147.3 i/s - 1.09x  slower
....

=== scripts/strings/substrings

.*Source*
[%collapsible]
====
[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.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin24.2.0]
Warming up --------------------------------------
       #sub (string)   720.839k i/100ms
        #sub (regex)   486.003k i/100ms
      #gsub (string)   504.617k i/100ms
       #gsub (regex)   264.917k i/100ms
                 #tr     1.303M i/100ms
Calculating -------------------------------------
       #sub (string)      7.516M (± 0.8%) i/s  (133.05 ns/i) -     38.204M in   5.083576s
        #sub (regex)      5.268M (± 0.7%) i/s  (189.83 ns/i) -     26.730M in   5.074524s
      #gsub (string)      5.380M (± 0.6%) i/s  (185.89 ns/i) -     27.249M in   5.065536s
       #gsub (regex)      2.822M (± 0.8%) i/s  (354.38 ns/i) -     14.306M in   5.069954s
                 #tr     13.969M (± 0.5%) i/s   (71.59 ns/i) -     70.343M in   5.035839s

Comparison:
                 #tr: 13968882.9 i/s
       #sub (string):  7515708.4 i/s - 1.86x  slower
      #gsub (string):  5379557.6 i/s - 2.60x  slower
        #sub (regex):  5267767.1 i/s - 2.65x  slower
       #gsub (regex):  2821821.0 i/s - 4.95x  slower
....

=== scripts/thens

.*Source*
[%collapsible]
====
[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.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin24.2.0]
Warming up --------------------------------------
            standard   520.294k i/100ms
                then   488.597k i/100ms
Calculating -------------------------------------
            standard      5.408M (± 0.8%) i/s  (184.93 ns/i) -     27.055M in   5.003534s
                then      5.043M (± 0.7%) i/s  (198.29 ns/i) -     25.407M in   5.038137s

Comparison:
            standard:  5407583.0 i/s
                then:  5043181.2 i/s - 1.07x  slower
....

=== scripts/values/inheritance

.*Source*
[%collapsible]
====
[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.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin24.2.0]
Warming up --------------------------------------
              Struct   538.804k i/100ms
            Subclass   518.538k i/100ms
Calculating -------------------------------------
              Struct      5.886M (± 1.3%) i/s  (169.90 ns/i) -     29.634M in   5.035664s
            Subclass      5.620M (± 1.0%) i/s  (177.92 ns/i) -     28.520M in   5.074817s

Comparison:
              Struct:  5885853.0 i/s
            Subclass:  5620422.5 i/s - 1.05x  slower
....

=== scripts/values/initialization

.*Source*
[%collapsible]
====
[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.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin24.2.0]
Warming up --------------------------------------
   Data (positional)   353.466k i/100ms
      Data (keyword)   350.442k i/100ms
       Data (custom)   318.060k i/100ms
 Struct (positional)     1.131M i/100ms
    Struct (keyword)   362.009k i/100ms
     Struct (custom)   352.839k i/100ms
          OpenStruct    11.114k i/100ms
          Dry Struct   124.525k i/100ms
Calculating -------------------------------------
   Data (positional)      3.719M (± 1.1%) i/s  (268.90 ns/i) -     18.734M in   5.038109s
      Data (keyword)      3.874M (± 2.0%) i/s  (258.16 ns/i) -     19.625M in   5.068537s
       Data (custom)      3.358M (± 1.7%) i/s  (297.78 ns/i) -     16.857M in   5.021195s
 Struct (positional)     12.121M (± 1.8%) i/s   (82.50 ns/i) -     61.063M in   5.039431s
    Struct (keyword)      3.805M (± 3.9%) i/s  (262.83 ns/i) -     19.186M in   5.050768s
     Struct (custom)      3.676M (± 1.6%) i/s  (272.02 ns/i) -     18.700M in   5.088300s
          OpenStruct    109.497k (± 2.8%) i/s    (9.13 μs/i) -    555.700k in   5.079350s
          Dry Struct      1.306M (± 0.9%) i/s  (765.65 ns/i) -      6.600M in   5.053588s

Comparison:
 Struct (positional): 12121050.5 i/s
      Data (keyword):  3873563.1 i/s - 3.13x  slower
    Struct (keyword):  3804720.3 i/s - 3.19x  slower
   Data (positional):  3718834.2 i/s - 3.26x  slower
     Struct (custom):  3676182.7 i/s - 3.30x  slower
       Data (custom):  3358180.2 i/s - 3.61x  slower
          Dry Struct:  1306074.7 i/s - 9.28x  slower
          OpenStruct:   109496.8 i/s - 110.70x  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*
[%collapsible]
====
[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.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin24.2.0]
Warming up --------------------------------------
                Data     4.138M i/100ms
              Struct     4.402M i/100ms
          OpenStruct     2.431M i/100ms
          Dry Struct     3.723M i/100ms
Calculating -------------------------------------
                Data     67.301M (± 2.9%) i/s   (14.86 ns/i) -    339.284M in   5.046842s
              Struct     67.277M (± 0.4%) i/s   (14.86 ns/i) -    338.976M in   5.038558s
          OpenStruct     31.752M (± 0.1%) i/s   (31.49 ns/i) -    160.441M in   5.052954s
          Dry Struct     46.533M (± 0.2%) i/s   (21.49 ns/i) -    234.527M in   5.040063s

Comparison:
                Data: 67301380.0 i/s
              Struct: 67277416.8 i/s - same-ish: difference falls within error
          Dry Struct: 46532814.0 i/s - 1.45x  slower
          OpenStruct: 31751956.0 i/s - 2.12x  slower
....

=== scripts/values/writing

.*Source*
[%collapsible]
====
[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.4.1 (2024-12-25 revision 48d4efcb85) +YJIT +PRISM [arm64-darwin24.2.0]
Warming up --------------------------------------
                Data   365.681k i/100ms
              Struct     4.017M i/100ms
          OpenStruct     1.970M i/100ms
Calculating -------------------------------------
                Data      3.818M (± 1.0%) i/s  (261.92 ns/i) -     19.381M in   5.076819s
              Struct     53.012M (± 0.3%) i/s   (18.86 ns/i) -    265.090M in   5.000654s
          OpenStruct     24.920M (± 0.2%) i/s   (40.13 ns/i) -    126.093M in   5.059884s

Comparison:
              Struct: 53011585.3 i/s
          OpenStruct: 24920314.7 i/s - 2.13x  slower
                Data:  3817956.9 i/s - 13.88x  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].

Owner

  • Name: Brooke Kuhlmann
  • Login: bkuhlmann
  • Kind: user
  • Location: Boulder, CO USA
  • Company: Alchemists

Quality over quantity.

Citation (CITATION.cff)

cff-version: 1.2.0
message: Please use the following metadata when citing this project in your work.
title: Benchmarks
abstract: A collection of micro benchmarks.
version: 5.4.0
license: Hippocratic-2.1
date-released: 2025-07-15
authors:
  - family-names: Kuhlmann
    given-names: Brooke
    affiliation: Alchemists
    orcid: https://orcid.org/0000-0002-5810-6268
keywords:
 - ruby
 - benchmarks
 - performance
repository-code: https://github.com/bkuhlmann/benchmarks
repository-artifact: https://alchemists.io/projects/benchmarks
url: https://alchemists.io/projects/benchmarks

GitHub Events

Total
  • Watch event: 1
  • Delete event: 229
  • Push event: 40
  • Create event: 65
Last Year
  • Watch event: 1
  • Delete event: 229
  • Push event: 40
  • Create event: 65

Issues and Pull Requests

Last synced: 10 months ago

All Time
  • Total issues: 0
  • Total pull requests: 0
  • Average time to close issues: N/A
  • Average time to close pull requests: N/A
  • Total issue authors: 0
  • Total pull request authors: 0
  • Average comments per issue: 0
  • Average comments per pull request: 0
  • Merged pull requests: 0
  • Bot issues: 0
  • Bot pull requests: 0
Past Year
  • Issues: 0
  • Pull requests: 0
  • Average time to close issues: N/A
  • Average time to close pull requests: N/A
  • Issue authors: 0
  • Pull request authors: 0
  • Average comments per issue: 0
  • Average comments per pull request: 0
  • Merged pull requests: 0
  • Bot issues: 0
  • Bot pull requests: 0
Top Authors
Issue Authors
Pull Request Authors
Top Labels
Issue Labels
Pull Request Labels

Dependencies

Gemfile rubygems
  • amazing_print ~> 1.4 development
  • caliber ~> 0.25 development
  • debug ~> 1.7 development
  • git-lint ~> 5.0 development
  • guard-rspec ~> 4.7 development
  • rake ~> 13.0 development
  • reek ~> 6.1 development
  • rspec ~> 3.12 development
  • simplecov ~> 0.22 development
  • refinements ~> 10.0