diff --git a/README.md b/README.md index 69c4dca..7fa8007 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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) ``` @@ -1704,6 +1811,49 @@ improvement. > :warning:
> `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] @@ -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) ``` diff --git a/code/general/loop-vs-while-true.rb b/code/general/loop-vs-while-true.rb index 33ae139..2c65971 100644 --- a/code/general/loop-vs-while-true.rb +++ b/code/general/loop-vs-while-true.rb @@ -1,8 +1,16 @@ -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 @@ -10,6 +18,14 @@ def fast end end +def fast + index = 0 + until false + break if index > NUMBER + index += 1 + end +end + def slow index = 0 loop do @@ -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 diff --git a/code/string/include-vs-match.rb b/code/string/include-vs-match.rb new file mode 100644 index 0000000..e417626 --- /dev/null +++ b/code/string/include-vs-match.rb @@ -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