https://github.com/bkuhlmann/benchmarks
A collection of micro benchmarks.
https://github.com/bkuhlmann/benchmarks
benchmark micro speed
Last synced: 6 months ago
JSON representation
A collection of micro benchmarks.
- Host: GitHub
- URL: https://github.com/bkuhlmann/benchmarks
- Owner: bkuhlmann
- License: other
- Created: 2019-02-02T00:22:51.000Z (over 6 years ago)
- Default Branch: main
- Last Pushed: 2025-03-25T02:09:49.000Z (7 months ago)
- Last Synced: 2025-04-11T21:41:34.729Z (6 months ago)
- Topics: benchmark, micro, speed
- Language: Ruby
- Homepage: https://alchemists.io/projects/benchmarks
- Size: 566 KB
- Stars: 5
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.adoc
- Funding: .github/FUNDING.yml
- License: LICENSE.adoc
- Citation: CITATION.cff
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 5.2.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: truerequire "bundler/inline"
gemfile true do
source "https://rubygems.org"gem "benchmark-ips"
enda = %w[one two three]
b = %w[four five six]Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2benchmark.report "#+" do
a + b
endbenchmark.report "#+=" do
duplicate = a.dup
duplicate += b
endbenchmark.report "#concat" do
a.dup.concat b
endbenchmark.report "#|" do
a | b
endbenchmark.report "#<< + #flatten" do
(a.dup << b).flatten
endbenchmark.report "splat + #flatten" do
[a, *b].flatten
endbenchmark.report "multi-splat" do
[*a, *b]
endbenchmark.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.082000sComparison:
#+: 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: truerequire "bundler/inline"
gemfile true do
source "https://rubygems.org"gem "benchmark-ips"
endlist = %w[one two three four five six seven eight nine ten]
pattern = /t/Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2benchmark.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.003928sComparison:
#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: truerequire "bundler/inline"
gemfile true do
source "https://rubygems.org"gem "benchmark-ips"
endmodule Test
def self.with_binding(end:) = binding.local_variable_get(:end)def self.with_pinning(end:) = {end:}[:end]
endBenchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2benchmark.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.011139sComparison:
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: truerequire "bundler/inline"
gemfile true do
source "https://rubygems.org"gem "benchmark-ips"
endCONSTANTS = Hash.new
module Constants
1_000.times { |index| CONSTANTS["EXAMPLE_#{index}"] = const_set "EXAMPLE_#{index}", index }
endBenchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2benchmark.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.028808sComparison:
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: truerequire "bundler/inline"
gemfile true do
source "https://rubygems.org"gem "benchmark-ips"
endrequire "delegate"
require "forwardable"module Echo
def self.call(message) = message
endclass ForwardExample
def initialize operation
@operation = operation
enddef call(...) = operation.call(...)
private
attr_reader :operation
endclass DelegateExample
extend Forwardabledelegate %i[call] => :operation
def initialize operation
@operation = operation
endprivate
attr_reader :operation
endclass SimpleExample < SimpleDelegator
endclass ClassExample < DelegateClass Echo
endmessage = "A test."
forward_example = ForwardExample.new Echo
deletate_example = DelegateExample.new Echo
simple_example = SimpleExample.new Echo
class_example = ClassExample.new EchoBenchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2benchmark.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.045399sComparison:
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: truerequire "bundler/inline"
gemfile true do
source "https://rubygems.org"gem "benchmark-ips"
endExample = Class.new do
def echo_implicit text
yield
text
enddef echo_implicit_guard text
yield if block_given?
text
enddef echo_explicit text, &block
yield block
text
enddef echo_explicit_guard text, &block
yield block if block
text
end
endblock_example = Example.new
lambda_example = -> text { text }
proc_example = proc { |text| text }Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2benchmark.report "Block (implicit)" do
block_example.echo_implicit("hi") { "test" }
endbenchmark.report "Block (implicit guard)" do
block_example.echo_implicit_guard("hi") { "test" }
endbenchmark.report "Block (explicit)" do
block_example.echo_explicit("hi") { "test" }
endbenchmark.report "Block (explicit guard)" do
block_example.echo_explicit_guard("hi") { "test" }
endbenchmark.report "Lambda" do
lambda_example.call "test"
endbenchmark.report "Proc" do
proc_example.call "test"
endbenchmark.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.061491sComparison:
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: truerequire "bundler/inline"
gemfile true do
source "https://rubygems.org"gem "benchmark-ips"
endclass_example = Class.new do
def self.call(first, second) = first + second
endmodule_example = Module.new do
module_functiondef call(first, second) = first + second
endfunction_example = -> first, second { first + second }
Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2benchmark.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.015900sComparison:
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: truerequire "bundler/inline"
gemfile true do
source "https://rubygems.org"gem "benchmark-ips"
endexample = {a: 1, b: 2, c: 3}
Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2benchmark.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.023111sComparison:
#[]: 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: truerequire "bundler/inline"
gemfile true do
source "https://rubygems.org"gem "benchmark-ips"
endextra = {b: 2}
Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2benchmark.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.053630sComparison:
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: truerequire "bundler/inline"
gemfile true do
source "https://rubygems.org"gem "benchmark-ips"
endnumbers = {
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: 2benchmark.report "Reduce" do
numbers.reduce({}) { |collection, (key, value)| collection.merge! value => key }
endbenchmark.report "With Object" do
numbers.each.with_object({}) { |(key, value), collection| collection[value] = key }
endbenchmark.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.090269sComparison:
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: truerequire "bundler/inline"
gemfile true do
source "https://rubygems.org"gem "benchmark-ips"
endcollection = (1..1_000).to_a
sum = 0Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2benchmark.report "for" do
for number in collection do
sum += number
end
endbenchmark.report "#each" do
collection.each { |number| sum += number }
endbenchmark.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.009754sComparison:
#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: truerequire "bundler/inline"
gemfile true do
source "https://rubygems.org"gem "benchmark-ips"
endrequire "forwardable"
Person = Class.new do
def initialize first, last
@first = first
@last = last
enddef full_name
"#{first} #{last}"
endprivate
attr_reader :first, :last
endExample = Class.new Person do
extend Forwardabledefine_method :unbound_full_name, Person.instance_method(:full_name)
delegate %i[full_name] => :persondef initialize first, last, person: Person.new(first, last)
super first, last
@person = person
enddef wrapped_full_name
person.full_name
endprivate
attr_reader :first, :last, :person
endexample = Example.new "Jill", "Doe"
Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2benchmark.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.074680sComparison:
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: truerequire "bundler/inline"
gemfile true do
source "https://rubygems.org"gem "benchmark-ips"
endExample = Class.new do
def initialize words
@words = words
@first_word = words.first
enddef direct_single
say first_word
enddef direct_multiple
words.each { |word| say word }
enddef proc_single
method(:say).call first_word
enddef proc_multiple
words.each { |word| method(:say).call word }
enddef method_to_proc_single
first_word.then(&method(:say))
enddef method_to_proc_multiple
words.each(&method(:say))
endprivate
attr_reader :words, :first_word
def say phrase
"You said: #{phrase}."
end
endexample = Example.new %w[one two three]
Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2benchmark.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.040560sComparison:
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: truerequire "bundler/inline"
gemfile true do
source "https://rubygems.org"gem "benchmark-ips"
endmodule Example
module_functiondef add(first = 1, second = 2) = first + second
endBenchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2benchmark.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.038936sComparison:
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: truerequire "bundler/inline"
gemfile true do
source "https://rubygems.org"gem "benchmark-ips"
endmodule Static
def self.call = rand > 0.5 ? one : twodef self.one = 1
def self.two = 2
endmodule 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
endBenchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2benchmark.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.026229sComparison:
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: truerequire "bundler/inline"
gemfile true do
source "https://rubygems.org"gem "bigdecimal"
gem "benchmark-ips"
endrequire "bigdecimal"
Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2benchmark.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.065332sComparison:
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: truerequire "bundler/inline"
gemfile true do
source "https://rubygems.org"gem "benchmark-ips"
endBenchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2benchmark.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.033138sComparison:
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: truerequire "bundler/inline"
gemfile true do
source "https://rubygems.org"gem "benchmark-ips"
endBenchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2benchmark.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.007114sComparison:
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: truerequire "bundler/inline"
gemfile true do
source "https://rubygems.org"gem "benchmark-ips"
endmodule Import
def dud = true
endBenchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2benchmark.report "With" do
Module.new { refine(String) { import_methods Import } }
endbenchmark.report "Without" do
Module.new { def dud = true }
endbenchmark.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.048344sComparison:
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: truerequire "bundler/inline"
gemfile true do
source "https://rubygems.org"gem "benchmark-ips"
endmodule Refines
refine String do
def dud = true
end
endclass With
using Refinesdef initialize value = "demo"
@value = value
end
endclass Without
def initialize value = "demo"
@value = value
end
endBenchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2benchmark.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.071267sComparison:
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: truerequire "bundler/inline"
gemfile true do
source "https://rubygems.org"gem "benchmark-ips"
endmodule Refines
refine String do
def dud = true
end
endmodule With
using Refinesdef self.call(value) = value.dud
endmodule Without
def self.call(value) = value
endvalue = "demo"
Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2benchmark.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.067107sComparison:
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: truerequire "bundler/inline"
gemfile true do
source "https://rubygems.org"gem "benchmark-ips"
endBenchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2benchmark.report "With" do
Module.new do
refine String do
def dud = true
end
end
endbenchmark.report "Without" do
Module.new do
def dud = true
end
endbenchmark.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.091996sComparison:
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: truerequire "bundler/inline"
gemfile true do
source "https://rubygems.org"gem "benchmark-ips"
endone = "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: 2benchmark.report "Implicit (<)" do
"One" "Two"
endbenchmark.report "Implicit (>)" do
"One" "Two" "Three" "Four" "Five" "Six" "Seven" "Eight" "Nine" "Ten"
endbenchmark.report "Interpolation (<)" do
"#{one} #{two}"
endbenchmark.report "Interpolation (>)" do
"#{one} #{two} #{three} #{four} #{five} #{six} #{seven} #{eight} #{nine} #{ten}"
endbenchmark.report "#+ (<)" do
one + " " + two
endbenchmark.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
endbenchmark.report "Array#join (<)" do
[one, two].join " "
endbenchmark.report "Array#join (>)" do
[one, two, three, four, five, six, seven, eight, nine, ten].join " "
endbenchmark.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.068889sComparison:
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: truerequire "bundler/inline"
gemfile true do
source "https://rubygems.org"gem "benchmark-ips"
endrequire "securerandom"
word = SecureRandom.alphanumeric 100
string_matcher = "a"
regex_matcher = /\Aa/Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2benchmark.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.084305sComparison:
#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: truerequire "bundler/inline"
gemfile true do
source "https://rubygems.org"gem "benchmark-ips"
endrequire "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: 2benchmark.report "Without Block" do
text.split(delimiter).grep(pattern)
endbenchmark.report "With Block" do
selections = []
text.split(delimiter) { |word| selections << word if word.match? pattern }
endbenchmark.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.039222sComparison:
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: truerequire "bundler/inline"
gemfile true do
source "https://rubygems.org"gem "benchmark-ips"
endexample = "example"
Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2benchmark.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.035839sComparison:
#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: truerequire "bundler/inline"
gemfile true do
source "https://rubygems.org"gem "benchmark-ips"
endBenchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2benchmark.report "standard" do
one, two = "one two".split
"#{one} + #{two} = #{one + two}"
endbenchmark.report "then" do
"one two".split.then { |one, two| "#{one} + #{two} = #{one + two}" }
endbenchmark.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.038137sComparison:
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: truerequire "bundler/inline"
gemfile true do
source "https://rubygems.org"gem "benchmark-ips"
endPlotStruct = Struct.new :x, :y
class PlotSubclass < Struct.new :x, :y
endstruct = -> { PlotStruct[x: 1, y: 2] }
subclass = -> { PlotSubclass[x: 1, y: 2] }Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2benchmark.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.074817sComparison:
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: truerequire "bundler/inline"
gemfile true do
source "https://rubygems.org"gem "benchmark-ips"
gem "dry-struct"
endWarning[: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
endStructDefault = 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
endmodule Types
include Dry.Types
endDryExample = 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
endBenchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2benchmark.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.053588sComparison:
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: truerequire "bundler/inline"
gemfile true do
source "https://rubygems.org"gem "benchmark-ips"
gem "dry-struct"
endrequire "ostruct"
DataExample = Data.define :to, :from
StructExample = Struct.new :to, :frommodule Types
include Dry.Types
endDryExample = Class.new Dry::Struct do
attribute :to, Types::Strict::String
attribute :from, Types::Strict::String
enddata = 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: 2benchmark.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.040063sComparison:
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: truerequire "bundler/inline"
gemfile true do
source "https://rubygems.org"gem "benchmark-ips"
endrequire "ostruct"
DataExample = Data.define :to, :from
StructExample = Struct.new :to, :fromdata = 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: 2benchmark.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.059884sComparison:
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].