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.
- Features
- Requirements
- Setup
- Usage
- scripts/arrays/concatenation
- scripts/arrays/search
- scripts/closures
- scripts/constants/lookup
- scripts/delegates
- scripts/hashes/lookup
- scripts/hashes/merge
- scripts/hashes/reduce
- scripts/loops
- scripts/methods/define_method
- scripts/methods/method_proc
- scripts/methods/send
- scripts/numerics
- scripts/refinements/import
- scripts/refinements/initialize
- scripts/refinements/message
- scripts/refinements/refine
- scripts/strings/concatenation
- scripts/strings/matching
- scripts/strings/split
- scripts/strings/substrings
- scripts/thens
- scripts/values/inheritance
- scripts/values/initialization
- scripts/values/reading
- scripts/values/writing
- Development
- Tests
- License
- Security
- Code of Conduct
- Contributions
- Versions
- Community
- Credits
-
Uses Benchmark IPS to calculate CPU/speed results.
-
Each script is independently executable.
To install, run:
git clone https://github.com/bkuhlmann/benchmarks.git
cd benchmarks
git checkout 4.3.0
bin/setup
All benchmark scripts are found within the scripts
folder and you can run any benchmark using a relative or absolute file path. Example:
scripts/strings/split
The following is a list of all benchmarks (source + results). Again, you run these locally or study the results provided instead.
Source
#! /usr/bin/env ruby
# frozen_string_literal: true
require "bundler/inline"
gemfile true do
source "https://rubygems.org"
gem "benchmark-ips"
end
a = %w[one two three]
b = %w[four five six]
Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2
benchmark.report "#+" do
a + b
end
benchmark.report "#+=" do
duplicate = a.dup
duplicate += b
end
benchmark.report "#concat" do
a.dup.concat b
end
benchmark.report "#|" do
a | b
end
benchmark.report "#<< + #flatten" do
(a.dup << b).flatten
end
benchmark.report "splat + #flatten" do
[a, *b].flatten
end
benchmark.report "multi-splat" do
[*a, *b]
end
benchmark.compare!
end
Benchmark
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0] Warming up -------------------------------------- #+ 1.109M i/100ms #+= 642.208k i/100ms #concat 640.592k i/100ms #| 462.891k i/100ms #<< + #flatten 190.264k i/100ms splat + #flatten 191.960k i/100ms multi-splat 595.693k i/100ms Calculating ------------------------------------- #+ 11.380M (± 6.3%) i/s - 57.673M in 5.088056s #+= 7.020M (± 7.5%) i/s - 35.321M in 5.060801s #concat 6.757M (± 4.3%) i/s - 33.951M in 5.033526s #| 4.589M (± 7.1%) i/s - 23.145M in 5.069256s #<< + #flatten 1.995M (± 7.8%) i/s - 10.084M in 5.083410s splat + #flatten 1.987M (± 7.7%) i/s - 9.982M in 5.053069s multi-splat 6.018M (± 5.4%) i/s - 30.380M in 5.063797s Comparison: #+: 11379928.4 i/s #+=: 7020046.0 i/s - 1.62x slower #concat: 6757373.6 i/s - 1.68x slower multi-splat: 6017673.7 i/s - 1.89x slower #|: 4588614.9 i/s - 2.48x slower #<< + #flatten: 1995390.6 i/s - 5.70x slower splat + #flatten: 1986837.6 i/s - 5.73x slower
Source
#! /usr/bin/env ruby
# frozen_string_literal: true
require "bundler/inline"
gemfile true do
source "https://rubygems.org"
gem "benchmark-ips"
end
list = %w[one two three four five six seven eight nine ten]
pattern = /t/
Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2
benchmark.report("#grep") { list.grep pattern }
benchmark.report("#select") { list.select { |value| value.match? pattern } }
benchmark.compare!
end
Benchmark
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0] Warming up -------------------------------------- #grep 173.406k i/100ms #select 185.779k i/100ms Calculating ------------------------------------- #grep 1.771M (± 6.7%) i/s - 8.844M in 5.016015s #select 1.912M (± 5.5%) i/s - 9.661M in 5.067631s Comparison: #select: 1912344.1 i/s #grep: 1770866.9 i/s - same-ish: difference falls within error
Source
#! /usr/bin/env ruby
# frozen_string_literal: true
require "bundler/inline"
gemfile true do
source "https://rubygems.org"
gem "benchmark-ips"
end
Example = Class.new do
def echo_implicit text
yield
text
end
def echo_implicit_guard text
yield if block_given?
text
end
def echo_explicit text, &block
yield block
text
end
def echo_explicit_guard text, &block
yield block if block
text
end
end
block_example = Example.new
lambda_example = -> text { text }
proc_example = proc { |text| text }
Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2
benchmark.report "Block (implicit)" do
block_example.echo_implicit("hi") { "test" }
end
benchmark.report "Block (implicit guard)" do
block_example.echo_implicit_guard("hi") { "test" }
end
benchmark.report "Block (explicit)" do
block_example.echo_explicit("hi") { "test" }
end
benchmark.report "Block (explicit guard)" do
block_example.echo_explicit_guard("hi") { "test" }
end
benchmark.report "Lambda" do
lambda_example.call "test"
end
benchmark.report "Proc" do
proc_example.call "test"
end
benchmark.compare!
end
Benchmark
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0] Warming up -------------------------------------- Block (implicit) 3.719M i/100ms Block (implicit guard) 3.997M i/100ms Block (explicit) 595.448k i/100ms Block (explicit guard) 597.963k i/100ms Lambda 2.428M i/100ms Proc 2.563M i/100ms Calculating ------------------------------------- Block (implicit) 59.804M (± 1.0%) i/s - 301.203M in 5.037053s Block (implicit guard) 59.083M (± 1.2%) i/s - 295.765M in 5.006599s Block (explicit) 6.377M (± 6.6%) i/s - 32.154M in 5.065513s Block (explicit guard) 6.365M (± 5.7%) i/s - 32.290M in 5.090170s Lambda 34.880M (± 1.2%) i/s - 174.833M in 5.013192s Proc 35.295M (± 1.9%) i/s - 176.865M in 5.012838s Comparison: Block (implicit): 59803662.6 i/s Block (implicit guard): 59083369.8 i/s - same-ish: difference falls within error Proc: 35294734.0 i/s - 1.69x slower Lambda: 34879827.8 i/s - 1.71x slower Block (explicit): 6377470.8 i/s - 9.38x slower Block (explicit guard): 6365264.1 i/s - 9.40x slower
Source
#! /usr/bin/env ruby
# frozen_string_literal: true
require "bundler/inline"
gemfile true do
source "https://rubygems.org"
gem "benchmark-ips"
end
CONSTANTS = Hash.new
module Constants
1_000.times { |index| CONSTANTS["EXAMPLE_#{index}"] = const_set "EXAMPLE_#{index}", index }
end
Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2
benchmark.report("#[]") { CONSTANTS["EXAMPLE_666"] }
benchmark.report("Module.get (symbol)") { Constants.const_get :EXAMPLE_666 }
benchmark.report("Module.get (string)") { Constants.const_get "EXAMPLE_666" }
benchmark.report("Object.get") { Object.const_get "Constants::EXAMPLE_666" }
benchmark.compare!
end
Benchmark
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0] Warming up -------------------------------------- #[] 3.380M i/100ms Module.get (symbol) 3.491M i/100ms Module.get (string) 1.706M i/100ms Object.get 1.068M i/100ms Calculating ------------------------------------- #[] 44.355M (± 0.2%) i/s - 223.075M in 5.029360s Module.get (symbol) 44.370M (± 0.1%) i/s - 223.435M in 5.035747s Module.get (string) 18.818M (± 0.5%) i/s - 95.510M in 5.075490s Object.get 11.076M (± 0.3%) i/s - 55.527M in 5.013445s Comparison: Module.get (symbol): 44369850.1 i/s #[]: 44354632.8 i/s - same-ish: difference falls within error Module.get (string): 18818346.2 i/s - 2.36x slower Object.get: 11075724.0 i/s - 4.01x slower
Source
#! /usr/bin/env ruby
# frozen_string_literal: true
require "bundler/inline"
gemfile true do
source "https://rubygems.org"
gem "benchmark-ips"
end
require "delegate"
require "forwardable"
module Echo
def self.call(message) = message
end
class ForwardExample
def initialize operation
@operation = operation
end
def call(...) = operation.call(...)
private
attr_reader :operation
end
class DelegateExample
extend Forwardable
delegate %i[call] => :operation
def initialize operation
@operation = operation
end
private
attr_reader :operation
end
class SimpleExample < SimpleDelegator
end
class ClassExample < DelegateClass Echo
end
message = "A test."
forward_example = ForwardExample.new Echo
deletate_example = DelegateExample.new Echo
simple_example = SimpleExample.new Echo
class_example = ClassExample.new Echo
Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2
benchmark.report("Forward") { forward_example.call message }
benchmark.report("Delegate") { deletate_example.call message }
benchmark.report("Simple Delegator") { simple_example.call message }
benchmark.report("Delegate Class") { class_example.call message }
benchmark.compare!
end
Benchmark
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0] Warming up -------------------------------------- Forward 1.132M i/100ms Delegate 1.017M i/100ms Simple Delegator 385.198k i/100ms Delegate Class 381.099k i/100ms Calculating ------------------------------------- Forward 13.380M (± 5.3%) i/s - 66.769M in 5.005398s Delegate 12.486M (± 3.0%) i/s - 63.053M in 5.054667s Simple Delegator 4.191M (± 7.0%) i/s - 21.186M in 5.077130s Delegate Class 4.174M (± 6.5%) i/s - 20.960M in 5.041277s Comparison: Forward: 13380268.0 i/s Delegate: 12486453.4 i/s - same-ish: difference falls within error Simple Delegator: 4191347.1 i/s - 3.19x slower Delegate Class: 4174068.4 i/s - 3.21x slower
Source
#! /usr/bin/env ruby
# frozen_string_literal: true
require "bundler/inline"
gemfile true do
source "https://rubygems.org"
gem "benchmark-ips"
end
example = {a: 1, b: 2, c: 3}
Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2
benchmark.report("#[]") { example[:b] }
benchmark.report("#fetch") { example.fetch :b }
benchmark.report("#fetch (default)") { example.fetch :b, "default" }
benchmark.report("#fetch (block)") { example.fetch(:b) { "default" } }
benchmark.report("#dig") { example.dig :b }
benchmark.compare!
end
Benchmark
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0] Warming up -------------------------------------- #[] 4.035M i/100ms #fetch 3.642M i/100ms #fetch (default) 3.661M i/100ms #fetch (block) 3.639M i/100ms #dig 3.747M i/100ms Calculating ------------------------------------- #[] 53.886M (± 0.1%) i/s - 270.328M in 5.016651s #fetch 45.478M (± 0.1%) i/s - 229.440M in 5.045124s #fetch (default) 45.677M (± 0.4%) i/s - 230.660M in 5.049861s #fetch (block) 44.675M (± 0.2%) i/s - 225.636M in 5.050641s #dig 50.390M (± 0.5%) i/s - 254.823M in 5.057135s Comparison: #[]: 53886256.9 i/s #dig: 50390168.1 i/s - 1.07x slower #fetch (default): 45677292.4 i/s - 1.18x slower #fetch: 45477689.6 i/s - 1.18x slower #fetch (block): 44674922.3 i/s - 1.21x slower
Source
#! /usr/bin/env ruby
# frozen_string_literal: true
require "bundler/inline"
gemfile true do
source "https://rubygems.org"
gem "benchmark-ips"
end
extra = {b: 2}
Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2
benchmark.report("Splat") { {a: 1, **extra} }
benchmark.report("Merge") { {a: 1}.merge extra }
benchmark.report("Merge!") { {a: 1}.merge! extra }
benchmark.report("Dup Merge!") { {a: 1}.dup.merge! extra }
benchmark.compare!
end
Benchmark
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0] Warming up -------------------------------------- Splat 977.474k i/100ms Merge 651.230k i/100ms Merge! 979.604k i/100ms Dup Merge! 525.213k i/100ms Calculating ------------------------------------- Splat 10.458M (± 7.0%) i/s - 52.784M in 5.072085s Merge 6.637M (± 2.2%) i/s - 33.213M in 5.006663s Merge! 10.624M (± 6.7%) i/s - 53.878M in 5.093783s Dup Merge! 5.400M (± 7.0%) i/s - 27.311M in 5.081979s Comparison: Merge!: 10624192.6 i/s Splat: 10457715.9 i/s - same-ish: difference falls within error Merge: 6636870.8 i/s - 1.60x slower Dup Merge!: 5399502.4 i/s - 1.97x slower
Source
#! /usr/bin/env ruby
# frozen_string_literal: true
require "bundler/inline"
gemfile true do
source "https://rubygems.org"
gem "benchmark-ips"
end
numbers = {
one: 1,
two: 2,
three: 3,
four: 4,
five: 5,
six: 6,
seven: 7,
eight: 8,
nine: 9,
ten: 10
}
Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2
benchmark.report "Reduce" do
numbers.reduce({}) { |collection, (key, value)| collection.merge! value => key }
end
benchmark.report "With Object" do
numbers.each.with_object({}) { |(key, value), collection| collection[value] = key }
end
benchmark.compare!
end
Benchmark
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0] Warming up -------------------------------------- Reduce 46.696k i/100ms With Object 86.950k i/100ms Calculating ------------------------------------- Reduce 469.369k (± 7.1%) i/s - 2.381M in 5.099251s With Object 893.855k (± 0.5%) i/s - 4.521M in 5.058420s Comparison: With Object: 893855.0 i/s Reduce: 469368.6 i/s - 1.90x slower
Source
#! /usr/bin/env ruby
# frozen_string_literal: true
require "bundler/inline"
gemfile true do
source "https://rubygems.org"
gem "benchmark-ips"
end
collection = (1..1_000).to_a
sum = 0
Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2
benchmark.report "for" do
for number in collection do
sum += number
end
end
benchmark.report "#each" do
collection.each { |number| sum += number }
end
benchmark.compare!
end
Benchmark
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0] Warming up -------------------------------------- for 6.369k i/100ms #each 6.583k i/100ms Calculating ------------------------------------- for 63.745k (± 0.1%) i/s - 324.819k in 5.095574s #each 65.617k (± 0.5%) i/s - 329.150k in 5.016325s Comparison: #each: 65617.4 i/s for: 63745.4 i/s - 1.03x slower
Source
#! /usr/bin/env ruby
# frozen_string_literal: true
require "bundler/inline"
gemfile true do
source "https://rubygems.org"
gem "benchmark-ips"
end
require "forwardable"
Person = Class.new do
def initialize first, last
@first = first
@last = last
end
def full_name
"#{first} #{last}"
end
private
attr_reader :first, :last
end
Example = Class.new Person do
extend Forwardable
define_method :unbound_full_name, Person.instance_method(:full_name)
delegate %i[full_name] => :person
def initialize first, last, person: Person.new(first, last)
super first, last
@person = person
end
def wrapped_full_name
person.full_name
end
private
attr_reader :first, :last, :person
end
example = Example.new "Jill", "Doe"
Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2
benchmark.report("Wrapped") { example.wrapped_full_name }
benchmark.report("Defined") { example.unbound_full_name }
benchmark.report("Delegated") { example.full_name }
benchmark.compare!
end
Benchmark
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0] Warming up -------------------------------------- Wrapped 1.128M i/100ms Defined 1.153M i/100ms Delegated 626.507k i/100ms Calculating ------------------------------------- Wrapped 12.039M (± 4.8%) i/s - 60.917M in 5.072433s Defined 12.270M (± 3.9%) i/s - 62.244M in 5.080966s Delegated 6.912M (± 6.2%) i/s - 34.458M in 5.005586s Comparison: Defined: 12269954.3 i/s Wrapped: 12039424.1 i/s - same-ish: difference falls within error Delegated: 6912331.3 i/s - 1.78x slower
Source
#! /usr/bin/env ruby
# frozen_string_literal: true
require "bundler/inline"
gemfile true do
source "https://rubygems.org"
gem "benchmark-ips"
end
Example = Class.new do
def initialize words
@words = words
@first_word = words.first
end
def direct_single
say first_word
end
def direct_multiple
words.each { |word| say word }
end
def proc_single
method(:say).call first_word
end
def proc_multiple
words.each { |word| method(:say).call word }
end
def method_to_proc_single
first_word.then(&method(:say))
end
def method_to_proc_multiple
words.each(&method(:say))
end
private
attr_reader :words, :first_word
def say phrase
"You said: #{phrase}."
end
end
example = Example.new %w[one two three]
Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2
benchmark.report("Direct (s)") { example.direct_single }
benchmark.report("Direct (m)") { example.direct_multiple }
benchmark.report("Proc (s)") { example.proc_single }
benchmark.report("Proc (m)") { example.proc_multiple }
benchmark.report("Method To Proc (s)") { example.method_to_proc_single }
benchmark.report("Method To Proc (m)") { example.method_to_proc_multiple }
benchmark.compare!
end
Benchmark
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0] Warming up -------------------------------------- Direct (s) 1.154M i/100ms Direct (m) 391.284k i/100ms Proc (s) 547.383k i/100ms Proc (m) 167.519k i/100ms Method To Proc (s) 260.729k i/100ms Method To Proc (m) 176.733k i/100ms Calculating ------------------------------------- Direct (s) 12.832M (± 4.1%) i/s - 64.611M in 5.043941s Direct (m) 4.159M (± 4.2%) i/s - 21.129M in 5.088977s Proc (s) 6.454M (± 6.0%) i/s - 32.296M in 5.023367s Proc (m) 1.830M (± 3.9%) i/s - 9.214M in 5.043206s Method To Proc (s) 2.874M (± 3.4%) i/s - 14.601M in 5.086253s Method To Proc (m) 1.962M (± 3.3%) i/s - 9.897M in 5.048962s Comparison: Direct (s): 12831929.9 i/s Proc (s): 6453824.9 i/s - 1.99x slower Direct (m): 4158707.8 i/s - 3.09x slower Method To Proc (s): 2874279.1 i/s - 4.46x slower Method To Proc (m): 1962181.8 i/s - 6.54x slower Proc (m): 1829728.8 i/s - 7.01x slower
Source
#! /usr/bin/env ruby
# frozen_string_literal: true
require "bundler/inline"
gemfile true do
source "https://rubygems.org"
gem "benchmark-ips"
end
module Static
def self.call = rand > 0.5 ? one : two
def self.one = 1
def self.two = 2
end
module Dynamic
def self.with_strings = public_send rand > 0.5 ? "one" : "two"
def self.with_symbols = public_send rand > 0.5 ? :one : :two
def self.one = 1
def self.two = 2
end
Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2
max = 1_000_000
benchmark.report("Static") { max.times { Static.call } }
benchmark.report("Dynamic (strings)") { max.times { Dynamic.with_strings } }
benchmark.report("Dynamic (symbols)") { max.times { Dynamic.with_symbols } }
benchmark.compare!
end
Benchmark
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0] Warming up -------------------------------------- Static 2.000 i/100ms Dynamic (strings) 1.000 i/100ms Dynamic (symbols) 1.000 i/100ms Calculating ------------------------------------- Static 27.041 (± 0.0%) i/s - 136.000 in 5.029371s Dynamic (strings) 11.600 (± 0.0%) i/s - 59.000 in 5.086066s Dynamic (symbols) 15.589 (± 0.0%) i/s - 78.000 in 5.003651s Comparison: Static: 27.0 i/s Dynamic (symbols): 15.6 i/s - 1.73x slower Dynamic (strings): 11.6 i/s - 2.33x slower
Source
#! /usr/bin/env ruby
# frozen_string_literal: true
require "bundler/inline"
gemfile true do
source "https://rubygems.org"
gem "bigdecimal"
gem "benchmark-ips"
end
require "bigdecimal"
Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2
benchmark.report("Integer") { 1 + 0 }
benchmark.report("Float") { 0.0001 + 0 }
benchmark.report("Rational") { (1 / 1000r) + 0 }
benchmark.report("BigDecimal") { BigDecimal("0.0001") + 0 }
benchmark.compare!
end
Benchmark
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0] Warming up -------------------------------------- Integer 4.945M i/100ms Float 3.898M i/100ms Rational 957.175k i/100ms BigDecimal 226.746k i/100ms Calculating ------------------------------------- Integer 75.084M (± 4.5%) i/s - 375.824M in 5.021257s Float 60.640M (± 0.6%) i/s - 304.039M in 5.013994s Rational 10.254M (± 0.6%) i/s - 51.687M in 5.040700s BigDecimal 2.332M (± 4.5%) i/s - 11.791M in 5.066520s Comparison: Integer: 75083686.6 i/s Float: 60640359.1 i/s - 1.24x slower Rational: 10254439.4 i/s - 7.32x slower BigDecimal: 2332151.1 i/s - 32.20x slower
Source
#! /usr/bin/env ruby
# frozen_string_literal: true
require "bundler/inline"
gemfile true do
source "https://rubygems.org"
gem "benchmark-ips"
end
module Import
def dud = true
end
Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2
benchmark.report "With" do
Module.new { refine(String) { import_methods Import } }
end
benchmark.report "Without" do
Module.new { def dud = true }
end
benchmark.compare!
end
Benchmark
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0] Warming up -------------------------------------- With 1.438k i/100ms Without 365.822k i/100ms Calculating ------------------------------------- With 21.970k (±190.8%) i/s - 43.140k in 5.067653s Without 3.636M (± 7.1%) i/s - 18.291M in 5.054830s Comparison: Without: 3635623.1 i/s With: 21969.9 i/s - 165.48x slower
Source
#! /usr/bin/env ruby
# frozen_string_literal: true
require "bundler/inline"
gemfile true do
source "https://rubygems.org"
gem "benchmark-ips"
end
module Refines
refine String do
def dud = true
end
end
class With
using Refines
def initialize value = "demo"
@value = value
end
end
class Without
def initialize value = "demo"
@value = value
end
end
Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2
benchmark.report("With") { With.new }
benchmark.report("Without") { Without.new }
benchmark.compare!
end
Benchmark
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0] Warming up -------------------------------------- With 1.118M i/100ms Without 1.067M i/100ms Calculating ------------------------------------- With 12.286M (± 5.5%) i/s - 61.467M in 5.019067s Without 11.971M (± 5.7%) i/s - 59.775M in 5.010076s Comparison: With: 12285927.1 i/s Without: 11970917.9 i/s - same-ish: difference falls within error
Source
#! /usr/bin/env ruby
# frozen_string_literal: true
require "bundler/inline"
gemfile true do
source "https://rubygems.org"
gem "benchmark-ips"
end
module Refines
refine String do
def dud = true
end
end
module With
using Refines
def self.call(value) = value.dud
end
module Without
def self.call(value) = value
end
value = "demo"
Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2
benchmark.report("With") { With.call value }
benchmark.report("Without") { Without.call value }
benchmark.compare!
end
Benchmark
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0] Warming up -------------------------------------- With 2.681M i/100ms Without 3.796M i/100ms Calculating ------------------------------------- With 37.893M (± 1.2%) i/s - 190.353M in 5.024141s Without 59.787M (± 4.2%) i/s - 299.874M in 5.024598s Comparison: Without: 59786895.6 i/s With: 37893449.5 i/s - 1.58x slower
Source
#! /usr/bin/env ruby
# frozen_string_literal: true
require "bundler/inline"
gemfile true do
source "https://rubygems.org"
gem "benchmark-ips"
end
Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2
benchmark.report "With" do
Module.new do
refine String do
def dud = true
end
end
end
benchmark.report "Without" do
Module.new do
def dud = true
end
end
benchmark.compare!
end
Benchmark
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0] Warming up -------------------------------------- With 1.497k i/100ms Without 337.437k i/100ms Calculating ------------------------------------- With 21.225k (±192.4%) i/s - 35.928k in 5.093624s Without 3.284M (± 8.5%) i/s - 16.534M in 5.067713s Comparison: Without: 3284418.0 i/s With: 21224.8 i/s - 154.74x slower
Source
#! /usr/bin/env ruby
# frozen_string_literal: true
require "bundler/inline"
gemfile true do
source "https://rubygems.org"
gem "benchmark-ips"
end
one = "One"
two = "Two"
three = "Three"
four = "Four"
five = "Five"
six = "Six"
seven = "Seven"
eight = "Eight"
nine = "Nine"
ten = "Ten"
Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2
benchmark.report "Implicit (<)" do
"One" "Two"
end
benchmark.report "Implicit (>)" do
"One" "Two" "Three" "Four" "Five" "Six" "Seven" "Eight" "Nine" "Ten"
end
benchmark.report "Interpolation (<)" do
"#{one} #{two}"
end
benchmark.report "Interpolation (>)" do
"#{one} #{two} #{three} #{four} #{five} #{six} #{seven} #{eight} #{nine} #{ten}"
end
benchmark.report "#+ (<)" do
one + " " + two
end
benchmark.report "#+ (>)" do
one + " " + two + " " + three + " " + four + " " + five + " " + six + " " + seven + " " +
eight + " " + nine + " " + ten
end
# WARNING: Mutation.
benchmark.report "#concat (<)" do
one.dup.concat two
end
# WARNING: Mutation.
benchmark.report "#concat (>)" do
one.dup.concat two, three, four, five, six, seven, eight, nine, ten
end
# WARNING: Mutation.
benchmark.report "#<< (<)" do
one.dup << two
end
# WARNING: Mutation.
benchmark.report "#<< (>)" do
one.dup << two << three << four << five << six << seven << eight << nine << ten
end
benchmark.report "Array#join (<)" do
[one, two].join " "
end
benchmark.report "Array#join (>)" do
[one, two, three, four, five, six, seven, eight, nine, ten].join " "
end
benchmark.compare!
end
Benchmark
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0] Warming up -------------------------------------- Implicit (<) 4.372M i/100ms Implicit (>) 4.448M i/100ms Interpolation (<) 1.208M i/100ms Interpolation (>) 367.925k i/100ms #+ (<) 823.216k i/100ms #+ (>) 80.833k i/100ms #concat (<) 1.369M i/100ms #concat (>) 291.617k i/100ms #<< (<) 1.479M i/100ms #<< (>) 459.494k i/100ms Array#join (<) 722.879k i/100ms Array#join (>) 307.222k i/100ms Calculating ------------------------------------- Implicit (<) 75.685M (± 1.7%) i/s - 380.338M in 5.026812s Implicit (>) 75.105M (± 1.8%) i/s - 378.120M in 5.036297s Interpolation (<) 12.722M (± 5.7%) i/s - 64.005M in 5.047960s Interpolation (>) 3.868M (± 5.9%) i/s - 19.500M in 5.057509s #+ (<) 8.701M (± 7.1%) i/s - 43.630M in 5.038462s #+ (>) 837.724k (± 7.7%) i/s - 4.203M in 5.048670s #concat (<) 14.496M (± 8.5%) i/s - 72.583M in 5.045357s #concat (>) 2.959M (± 8.8%) i/s - 14.872M in 5.063294s #<< (<) 15.944M (± 8.0%) i/s - 79.888M in 5.043131s #<< (>) 4.739M (± 9.0%) i/s - 23.894M in 5.082877s Array#join (<) 7.392M (± 7.9%) i/s - 36.867M in 5.018258s Array#join (>) 3.275M (± 7.8%) i/s - 16.283M in 5.000238s Comparison: Implicit (<): 75684765.3 i/s Implicit (>): 75104861.7 i/s - same-ish: difference falls within error #<< (<): 15944068.0 i/s - 4.75x slower #concat (<): 14496048.1 i/s - 5.22x slower Interpolation (<): 12722385.9 i/s - 5.95x slower #+ (<): 8700892.4 i/s - 8.70x slower Array#join (<): 7392065.5 i/s - 10.24x slower #<< (>): 4738942.8 i/s - 15.97x slower Interpolation (>): 3867990.4 i/s - 19.57x slower Array#join (>): 3274997.2 i/s - 23.11x slower #concat (>): 2959275.8 i/s - 25.58x slower #+ (>): 837724.4 i/s - 90.35x slower
Source
#! /usr/bin/env ruby
# frozen_string_literal: true
require "bundler/inline"
gemfile true do
source "https://rubygems.org"
gem "benchmark-ips"
end
require "securerandom"
word = SecureRandom.alphanumeric 100
string_matcher = "a"
regex_matcher = /\Aa/
Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2
benchmark.report("#match?") { word.match? regex_matcher }
benchmark.report("#=~") { word =~ regex_matcher }
benchmark.report("#start_with? (String)") { word.start_with? string_matcher }
benchmark.report("#start_with? (Regex)") { word.start_with? regex_matcher }
benchmark.report("#end_with?") { word.end_with? string_matcher }
benchmark.compare!
end
Benchmark
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0] Warming up -------------------------------------- #match? 2.422M i/100ms #=~ 742.580k i/100ms #start_with? (String) 2.965M i/100ms #start_with? (Regex) 679.369k i/100ms #end_with? 3.069M i/100ms Calculating ------------------------------------- #match? 28.797M (± 5.0%) i/s - 145.346M in 5.060238s #=~ 7.620M (± 9.1%) i/s - 37.872M in 5.008839s #start_with? (String) 34.312M (± 5.0%) i/s - 171.982M in 5.024675s #start_with? (Regex) 7.464M (± 7.4%) i/s - 37.365M in 5.034025s #end_with? 37.141M (± 0.5%) i/s - 187.219M in 5.040811s Comparison: #end_with?: 37141486.2 i/s #start_with? (String): 34312493.3 i/s - 1.08x slower #match?: 28797216.7 i/s - 1.29x slower #=~: 7620209.0 i/s - 4.87x slower #start_with? (Regex): 7463669.2 i/s - 4.98x slower
Source
#! /usr/bin/env ruby
# frozen_string_literal: true
require "bundler/inline"
gemfile true do
source "https://rubygems.org"
gem "benchmark-ips"
end
require "securerandom"
words = Array.new(100_000) { SecureRandom.alphanumeric 10 }
delimiter = " "
text = words.join delimiter
pattern = /\Aa/
Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2
benchmark.report "Without Block" do
text.split(delimiter).grep(pattern)
end
benchmark.report "With Block" do
selections = []
text.split(delimiter) { |word| selections << word if word.match? pattern }
end
benchmark.compare!
end
Benchmark
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0] Warming up -------------------------------------- Without Block 13.000 i/100ms With Block 13.000 i/100ms Calculating ------------------------------------- Without Block 136.799 (± 2.2%) i/s - 689.000 in 5.039420s With Block 134.538 (± 2.2%) i/s - 676.000 in 5.026808s Comparison: Without Block: 136.8 i/s With Block: 134.5 i/s - same-ish: difference falls within error
Source
#! /usr/bin/env ruby
# frozen_string_literal: true
require "bundler/inline"
gemfile true do
source "https://rubygems.org"
gem "benchmark-ips"
end
example = "example"
Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2
benchmark.report("#sub (string)") { example.sub "x", "b" }
benchmark.report("#sub (regex)") { example.sub(/x/, "b") }
benchmark.report("#gsub (string)") { example.gsub "x", "b" }
benchmark.report("#gsub (regex)") { example.gsub(/x/, "b") }
benchmark.report("#tr") { example.tr "x", "b" }
benchmark.compare!
end
Benchmark
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0] Warming up -------------------------------------- #sub (string) 484.505k i/100ms #sub (regex) 344.692k i/100ms #gsub (string) 414.555k i/100ms #gsub (regex) 170.417k i/100ms #tr 1.007M i/100ms Calculating ------------------------------------- #sub (string) 5.449M (± 3.0%) i/s - 27.617M in 5.073218s #sub (regex) 4.021M (± 6.1%) i/s - 20.337M in 5.074883s #gsub (string) 4.463M (± 3.4%) i/s - 22.386M in 5.021307s #gsub (regex) 1.710M (± 8.6%) i/s - 8.521M in 5.018878s #tr 10.693M (± 2.8%) i/s - 54.352M in 5.087263s Comparison: #tr: 10692642.6 i/s #sub (string): 5448640.0 i/s - 1.96x slower #gsub (string): 4463049.2 i/s - 2.40x slower #sub (regex): 4020798.1 i/s - 2.66x slower #gsub (regex): 1710379.3 i/s - 6.25x slower
Source
#! /usr/bin/env ruby
# frozen_string_literal: true
require "bundler/inline"
gemfile true do
source "https://rubygems.org"
gem "benchmark-ips"
end
Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2
benchmark.report "standard" do
one, two = "one two".split
"#{one} + #{two} = #{one + two}"
end
benchmark.report "then" do
"one two".split.then { |one, two| "#{one} + #{two} = #{one + two}" }
end
benchmark.compare!
end
Benchmark
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0] Warming up -------------------------------------- standard 361.533k i/100ms then 340.568k i/100ms Calculating ------------------------------------- standard 3.566M (± 7.3%) i/s - 18.077M in 5.096489s then 3.366M (± 7.6%) i/s - 17.028M in 5.088830s Comparison: standard: 3566064.8 i/s then: 3365922.3 i/s - same-ish: difference falls within error
Source
#! /usr/bin/env ruby
# frozen_string_literal: true
require "bundler/inline"
gemfile true do
source "https://rubygems.org"
gem "benchmark-ips"
end
PlotStruct = Struct.new :x, :y
class PlotSubclass < Struct.new :x, :y
end
struct = -> { PlotStruct[x: 1, y: 2] }
subclass = -> { PlotSubclass[x: 1, y: 2] }
Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2
benchmark.report("Struct") { struct.call }
benchmark.report("Subclass") { subclass.call }
benchmark.compare!
end
Benchmark
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0] Warming up -------------------------------------- Struct 446.439k i/100ms Subclass 438.472k i/100ms Calculating ------------------------------------- Struct 4.925M (± 7.6%) i/s - 24.554M in 5.013174s Subclass 4.736M (± 7.8%) i/s - 23.677M in 5.027862s Comparison: Struct: 4924540.0 i/s Subclass: 4736109.1 i/s - same-ish: difference falls within error
Source
#! /usr/bin/env ruby
# frozen_string_literal: true
require "bundler/inline"
gemfile true do
source "https://rubygems.org"
gem "benchmark-ips"
gem "dry-struct"
end
Warning[:performance] = false
require "ostruct"
DataDefault = Data.define :a, :b, :c, :d, :e
DataCustom = Data.define :a, :b, :c, :d, :e do
def initialize a: 1, b: 2, c: 3, d: 4, e: 5
super
end
end
StructDefault = Struct.new :a, :b, :c, :d, :e
StructCustom = Struct.new :a, :b, :c, :d, :e do
def initialize a: 1, b: 2, c: 3, d: 4, e: 5
super
end
end
module Types
include Dry.Types
end
DryExample = Class.new Dry::Struct do
attribute :a, Types::Strict::Integer
attribute :b, Types::Strict::Integer
attribute :c, Types::Strict::Integer
attribute :d, Types::Strict::Integer
attribute :e, Types::Strict::Integer
end
Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2
benchmark.report("Data (positional)") { DataDefault[1, 2, 3, 4, 5] }
benchmark.report("Data (keyword)") { DataDefault[a: 1, b: 2, c: 3, d: 4, e: 5] }
benchmark.report("Data (custom)") { DataCustom.new }
benchmark.report("Struct (positional)") { StructDefault[1, 2, 3, 4, 5] }
benchmark.report("Struct (keyword)") { StructDefault[a: 1, b: 2, c: 3, d: 4, e: 5] }
benchmark.report("Struct (custom)") { StructCustom.new }
benchmark.report("OpenStruct") { OpenStruct.new a: 1, b: 2, c: 3, d: 4, e: 5 }
benchmark.report("Dry Struct") { DryExample[a: 1, b: 2, c: 3, d: 4, e: 5] }
benchmark.compare!
end
Benchmark
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0] Warming up -------------------------------------- Data (positional) 304.848k i/100ms Data (keyword) 317.019k i/100ms Data (custom) 224.669k i/100ms Struct (positional) 722.167k i/100ms Struct (keyword) 302.062k i/100ms Struct (custom) 292.850k i/100ms OpenStruct 743.000 i/100ms Dry Struct 111.304k i/100ms Calculating ------------------------------------- Data (positional) 3.120M (± 9.7%) i/s - 15.547M in 5.025792s Data (keyword) 3.191M (± 9.7%) i/s - 15.851M in 5.008582s Data (custom) 2.226M (± 3.6%) i/s - 11.233M in 5.054091s Struct (positional) 6.869M (±10.4%) i/s - 34.664M in 5.098769s Struct (keyword) 3.059M (± 9.6%) i/s - 15.405M in 5.077727s Struct (custom) 2.945M (±11.4%) i/s - 14.642M in 5.031281s OpenStruct 1.856k (±24.0%) i/s - 9.659k in 5.485483s Dry Struct 1.123M (±11.8%) i/s - 5.565M in 5.029750s Comparison: Struct (positional): 6868838.9 i/s Data (keyword): 3191343.7 i/s - 2.15x slower Data (positional): 3120155.8 i/s - 2.20x slower Struct (keyword): 3059412.6 i/s - 2.25x slower Struct (custom): 2945238.4 i/s - 2.33x slower Data (custom): 2225751.7 i/s - 3.09x slower Dry Struct: 1123063.3 i/s - 6.12x slower OpenStruct: 1855.9 i/s - 3701.01x slower ℹ️ `Data` is fastest when members are small (like three or less) but performance degrades when more members are added (like five or more). This is because `Data` always initializes with a `Hash` which is not the case with a `Struct`. Additionally, passing keyword arguments to/from Ruby to Ruby is optimized while to/from Ruby/C is not.
Source
#! /usr/bin/env ruby
# frozen_string_literal: true
require "bundler/inline"
gemfile true do
source "https://rubygems.org"
gem "benchmark-ips"
gem "dry-struct"
end
require "ostruct"
DataExample = Data.define :to, :from
StructExample = Struct.new :to, :from
module Types
include Dry.Types
end
DryExample = Class.new Dry::Struct do
attribute :to, Types::Strict::String
attribute :from, Types::Strict::String
end
data = DataExample[to: "Rick", from: "Morty"]
struct = StructExample[to: "Rick", from: "Morty"]
open_struct = OpenStruct.new to: "Rick", from: "Morty"
dry_struct = DryExample[to: "Rick", from: "Morty"]
Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2
benchmark.report("Data") { data.to }
benchmark.report("Struct") { struct.to }
benchmark.report("OpenStruct") { open_struct.to }
benchmark.report("Dry Struct") { dry_struct.to }
benchmark.compare!
end
Benchmark
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0] Warming up -------------------------------------- Data 4.313M i/100ms Struct 3.960M i/100ms OpenStruct 3.419M i/100ms Dry Struct 3.646M i/100ms Calculating ------------------------------------- Data 72.832M (± 0.1%) i/s - 366.637M in 5.034027s Struct 68.730M (± 0.4%) i/s - 344.554M in 5.013242s OpenStruct 49.555M (± 0.1%) i/s - 249.580M in 5.036400s Dry Struct 49.635M (± 0.1%) i/s - 251.578M in 5.068604s Comparison: Data: 72831756.3 i/s Struct: 68729971.7 i/s - 1.06x slower Dry Struct: 49634683.0 i/s - 1.47x slower OpenStruct: 49555343.3 i/s - 1.47x slower
Source
#! /usr/bin/env ruby
# frozen_string_literal: true
require "bundler/inline"
gemfile true do
source "https://rubygems.org"
gem "benchmark-ips"
end
require "ostruct"
DataExample = Data.define :to, :from
StructExample = Struct.new :to, :from
data = DataExample[to: "Rick", from: "Morty"]
struct = StructExample[to: "Rick", from: "Morty"]
open_struct = OpenStruct.new to: "Rick", from: "Morty"
Benchmark.ips do |benchmark|
benchmark.config time: 5, warmup: 2
benchmark.report("Data") { data.with from: "Summer" }
benchmark.report("Struct") { struct.from = "Summer" }
benchmark.report("OpenStruct") { open_struct.from = "Summer" }
benchmark.compare!
end
Benchmark
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23.4.0] Warming up -------------------------------------- Data 265.287k i/100ms Struct 4.274M i/100ms OpenStruct 2.870M i/100ms Calculating ------------------------------------- Data 2.721M (± 8.0%) i/s - 13.530M in 5.002940s Struct 55.482M (± 1.5%) i/s - 277.793M in 5.008024s OpenStruct 34.810M (± 0.2%) i/s - 175.090M in 5.029864s Comparison: Struct: 55482353.9 i/s OpenStruct: 34810159.3 i/s - 1.59x slower Data: 2721426.3 i/s - 20.39x slower
To contribute, run:
git clone https://github.com/bkuhlmann/benchmarks.git
cd benchmarks
bin/setup
To render documentation for all benchmark scripts, run:
bin/render
This is the same script used to update the documentation within this README.
-
Built with Rubysmith.
-
Engineered by Brooke Kuhlmann.