Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
192 changes: 191 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,90 @@ Custom exception: Kernel#raise: 2598894.6 i/s
Custom exception: E2MM#Raise: 88322.1 i/s - 29.43x slower
```

##### `loop` vs `while true` [code](code/general/loop-vs-while-true.rb)
##### `begin...end until` vs `while true` vs `until false` vs `loop` vs infinite range [code](code/general/loop-vs-while-true.rb)

The first run only had `while true` and `loop`.
The second is from #143, which added `begin...end until`, and the third from #228, which added `until false` and the infinite range.

```
$ ruby -v code/general/loop-vs-while-true.rb
ruby 2.2.3p173 (2015-08-18 revision 51636) [x86_64-linux]

Calculating -------------------------------------
While Loop 1.000 i/100ms
Kernel loop 1.000 i/100ms
-------------------------------------------------
While Loop 0.536 (± 0.0%) i/s - 3.000 in 5.593042s
Kernel loop 0.223 (± 0.0%) i/s - 2.000 in 8.982355s

Comparison:
While Loop: 0.5 i/s
Kernel loop: 0.2 i/s - 2.41x slower
```

```
$ ruby -v code/general/loop-vs-while-true.rb
ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-linux]
Warming up --------------------------------------
Begin Until 1.000 i/100ms
While Loop 1.000 i/100ms
Kernel loop 1.000 i/100ms
Calculating -------------------------------------
Begin Until 0.211 (± 0.0%) i/s - 2.000 in 9.469782s
While Loop 0.192 (± 0.0%) i/s - 1.000 in 5.212417s
Kernel loop 0.072 (± 0.0%) i/s - 1.000 in 13.921672s

Comparison:
Begin Until: 0.2 i/s
While Loop: 0.2 i/s - 1.10x slower
Kernel loop: 0.1 i/s - 2.94x slower
```

```
$ ruby -v code/general/loop-vs-while-true-vs-until-false-vs-infinite-range.rb
ruby 3.3.0dev (2023-11-12 master 60e19a0b5f) [x86_64-linux]

Warming up --------------------------------------
While loop 1.000 i/100ms
Until loop 1.000 i/100ms
Kernel loop 1.000 i/100ms
Infinite range 1.000 i/100ms
Calculating -------------------------------------
While loop 0.600 (± 0.0%) i/s - 3.000 in 5.002930s
Until loop 0.594 (± 0.0%) i/s - 3.000 in 5.054004s
Kernel loop 0.258 (± 0.0%) i/s - 2.000 in 7.754873s
Infinite range 0.207 (± 0.0%) i/s - 2.000 in 9.664227s

Comparison:
While loop: 0.6 i/s
Until loop: 0.6 i/s - 1.01x slower
Kernel loop: 0.3 i/s - 2.33x slower
Infinite range: 0.2 i/s - 2.90x slower
```

```
$ ruby -v code/general/loop-vs-while-true.rb
ruby 3.4.10 (2026-06-30 revision 2b0b7728dc) +PRISM [aarch64-linux]
Warming up --------------------------------------
Begin Until 1.000 i/100ms
While Loop 1.000 i/100ms
Until loop 1.000 i/100ms
Kernel loop 1.000 i/100ms
Infinite range 1.000 i/100ms
Calculating -------------------------------------
Begin Until 0.715 (± 0.0%) i/s (1.40 s/i) - 4.000 in 5.593345s
While Loop 0.713 (± 0.0%) i/s (1.40 s/i) - 4.000 in 5.607034s
Until loop 0.710 (± 0.0%) i/s (1.41 s/i) - 4.000 in 5.634041s
Kernel loop 0.387 (± 0.0%) i/s (2.59 s/i) - 2.000 in 5.172241s
Infinite range 0.399 (± 0.0%) i/s (2.50 s/i) - 2.000 in 5.008409s

Comparison:
Begin Until: 0.7 i/s
While Loop: 0.7 i/s - 1.00x slower
Until loop: 0.7 i/s - 1.01x slower
Infinite range: 0.4 i/s - 1.79x slower
Kernel loop: 0.4 i/s - 1.85x slower
```

```
$ ruby -v code/general/loop-vs-while-true.rb
Expand All @@ -235,6 +318,30 @@ Comparison:
Kernel loop: 0.5 i/s - 2.52x slower
```

```
$ ruby -v code/general/loop-vs-while-true.rb
ruby 4.0.7 (2026-09-15 revision 229531a6cf) +PRISM [aarch64-linux]
Warming up --------------------------------------
Begin Until 1.000 i/100ms
While Loop 1.000 i/100ms
Until loop 1.000 i/100ms
Kernel loop 1.000 i/100ms
Infinite range 1.000 i/100ms
Calculating -------------------------------------
Begin Until 0.666 (± 0.0%) i/s (1.50 s/i) - 4.000 in 6.006927s
While Loop 0.687 (± 0.0%) i/s (1.46 s/i) - 4.000 in 5.826603s
Until loop 0.686 (± 0.0%) i/s (1.46 s/i) - 4.000 in 5.828591s
Kernel loop 0.520 (± 0.0%) i/s (1.92 s/i) - 3.000 in 5.767842s
Infinite range 0.386 (± 0.0%) i/s (2.59 s/i) - 2.000 in 5.184388s

Comparison:
While Loop: 0.7 i/s
Until loop: 0.7 i/s - 1.00x slower
Begin Until: 0.7 i/s - 1.03x slower
Kernel loop: 0.5 i/s - 1.32x slower
Infinite range: 0.4 i/s - 1.78x slower
```

##### `ancestors.include?` vs `<=` [code](code/general/inheritance-check.rb)

```
Expand Down Expand Up @@ -1704,6 +1811,49 @@ improvement.
> :warning: <br>
> `Regexp#===` is also faster than `String#match` but you need to switch the order of arguments.

```
$ ruby -v code/string/===-vs-=~-vs-match.rb
ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-darwin14]

Calculating -------------------------------------
String#=~ 98.184k i/100ms
Regexp#=== 92.382k i/100ms
String#match 83.601k i/100ms
-------------------------------------------------
String#=~ 2.442M (± 7.6%) i/s - 12.175M
Regexp#=== 2.259M (± 7.9%) i/s - 11.271M
String#match 1.840M (± 7.3%) i/s - 9.196M

Comparison:
String#=~: 2442335.1 i/s
Regexp#===: 2259277.3 i/s - 1.08x slower
String#match: 1839815.4 i/s - 1.33x slower
```

The next run is from #139, which added `String#include?` to this file; it now has its own entry below.

```
$ ruby -v code/string/===-vs-=~-vs-match.rb
ruby 2.4.2p198 (2017-09-14 revision 59899) [x86_64-darwin16]

Warming up --------------------------------------
String#include? 238.465k i/100ms
String#=~ 161.826k i/100ms
Regexp#=== 147.986k i/100ms
String#match 136.749k i/100ms
Calculating -------------------------------------
String#include? 5.961M (± 8.5%) i/s - 29.808M in 5.035615s
String#=~ 2.843M (± 8.1%) i/s - 14.241M in 5.040590s
Regexp#=== 2.689M (± 7.3%) i/s - 13.467M in 5.032779s
String#match 2.371M (± 5.2%) i/s - 11.897M in 5.031192s

Comparison:
String#include?: 5960785.4 i/s
String#=~: 2843384.2 i/s - 2.10x slower
Regexp#===: 2689290.6 i/s - 2.22x slower
String#match: 2370791.0 i/s - 2.51x slower
```

```
$ ruby -v code/string/===-vs-=~-vs-match.rb
ruby 4.0.0 (2025-12-25 revision 553f1675f3) +PRISM [arm64-darwin24]
Expand Down Expand Up @@ -1734,6 +1884,46 @@ Comparison:
See [#59](https://github.com/fastruby/fast-ruby/pull/59) and [#62](https://github.com/fastruby/fast-ruby/pull/62) for discussions.


##### `String#match?` vs `String#include?` vs `String#=~` for a fixed substring [code](code/string/include-vs-match.rb)

For a fixed substring, `String#include?` beats a Regexp with `=~`, and `String#match?` (Ruby 2.4+) is as fast or faster.

```
$ ruby -v code/string/include-vs-match.rb
ruby 3.4.10 (2026-06-30 revision 2b0b7728dc) +PRISM [aarch64-linux]
Warming up --------------------------------------
String#match? 1.325M i/100ms
String#include? 995.530k i/100ms
String#=~ 853.136k i/100ms
Calculating -------------------------------------
String#match? 13.827M (± 3.2%) i/s (72.32 ns/i) - 70.248M in 5.080506s
String#include? 9.974M (± 1.5%) i/s (100.26 ns/i) - 50.772M in 5.090292s
String#=~ 8.489M (± 4.8%) i/s (117.81 ns/i) - 42.657M in 5.025211s

Comparison:
String#match?: 13826876.4 i/s
String#include?: 9974287.2 i/s - 1.39x slower
String#=~: 8488559.2 i/s - 1.63x slower
```

```
$ ruby -v code/string/include-vs-match.rb
ruby 4.0.7 (2026-09-15 revision 229531a6cf) +PRISM [aarch64-linux]
Warming up --------------------------------------
String#match? 1.517M i/100ms
String#include? 1.283M i/100ms
String#=~ 1.004M i/100ms
Calculating -------------------------------------
String#match? 14.967M (± 3.4%) i/s (66.81 ns/i) - 75.870M in 5.069146s
String#include? 12.845M (± 0.5%) i/s (77.85 ns/i) - 65.441M in 5.094510s
String#=~ 10.039M (± 1.0%) i/s (99.61 ns/i) - 50.218M in 5.002306s

Comparison:
String#match?: 14967056.6 i/s
String#include?: 12845487.5 i/s - 1.17x slower
String#=~: 10038889.7 i/s - 1.49x slower
```

##### `String#gsub` vs `String#sub` vs `String#[]=` [code](code/string/gsub-vs-sub.rb)

```
Expand Down
38 changes: 34 additions & 4 deletions code/general/loop-vs-while-true.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,31 @@
require "benchmark/ips"
require 'benchmark/ips'

NUMBER = 100_000_000

def fast
# The three loops that test a condition tie on Ruby 4.0.
def fastest
index = 0
begin
index += 1
end until index > NUMBER
end

def faster
index = 0
while true
break if index > NUMBER
index += 1
end
end

def fast
index = 0
until false
break if index > NUMBER
index += 1
end
end

def slow
index = 0
loop do
Expand All @@ -18,8 +34,22 @@ def slow
end
end

# Endless ranges are Ruby 2.6+ syntax, so the method is defined from a string, which older Rubies never parse.
if RUBY_VERSION >= '2.6.0'
eval <<-RUBY
def slower
(0..).each do |index|
break if index > NUMBER
end
end
RUBY
end

Benchmark.ips do |x|
x.report("While Loop") { fast }
x.report("Kernel loop") { slow }
x.report('Begin Until') { fastest }
x.report('While Loop') { faster }
x.report('Until loop') { fast }
x.report('Kernel loop') { slow }
x.report('Infinite range') { slower } if RUBY_VERSION >= '2.6.0'
x.compare!
end
22 changes: 22 additions & 0 deletions code/string/include-vs-match.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
require 'benchmark/ips'

# For a fixed substring, String#include? beats a Regexp with =~.
# String#match? (Ruby 2.4+) is as fast as include? or faster on current CRuby.
def faster
'foo'.freeze.match?(/boo/)
end

def fast
'foo'.freeze.include?('boo')
end

def slow
'foo'.freeze =~ /boo/
end

Benchmark.ips do |x|
x.report('String#match?') { faster } if RUBY_VERSION >= '2.4.0'
x.report('String#include?') { fast }
x.report('String#=~') { slow }
x.compare!
end
Loading