Skip to content

Commit 3ff4aba

Browse files
authored
Merge pull request #246 from fastruby/feature/adopt-open-prs-3
Adopt three more open benchmark PRs
2 parents a61e0f0 + be66ea1 commit 3ff4aba

3 files changed

Lines changed: 247 additions & 5 deletions

File tree

‎README.md‎

Lines changed: 191 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,90 @@ Custom exception: Kernel#raise: 2598894.6 i/s
218218
Custom exception: E2MM#Raise: 88322.1 i/s - 29.43x slower
219219
```
220220

221-
##### `loop` vs `while true` [code](code/general/loop-vs-while-true.rb)
221+
##### `begin...end until` vs `while true` vs `until false` vs `loop` vs infinite range [code](code/general/loop-vs-while-true.rb)
222+
223+
The first run only had `while true` and `loop`.
224+
The second is from #143, which added `begin...end until`, and the third from #228, which added `until false` and the infinite range.
225+
226+
```
227+
$ ruby -v code/general/loop-vs-while-true.rb
228+
ruby 2.2.3p173 (2015-08-18 revision 51636) [x86_64-linux]
229+
230+
Calculating -------------------------------------
231+
While Loop 1.000 i/100ms
232+
Kernel loop 1.000 i/100ms
233+
-------------------------------------------------
234+
While Loop 0.536 (± 0.0%) i/s - 3.000 in 5.593042s
235+
Kernel loop 0.223 (± 0.0%) i/s - 2.000 in 8.982355s
236+
237+
Comparison:
238+
While Loop: 0.5 i/s
239+
Kernel loop: 0.2 i/s - 2.41x slower
240+
```
241+
242+
```
243+
$ ruby -v code/general/loop-vs-while-true.rb
244+
ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-linux]
245+
Warming up --------------------------------------
246+
Begin Until 1.000 i/100ms
247+
While Loop 1.000 i/100ms
248+
Kernel loop 1.000 i/100ms
249+
Calculating -------------------------------------
250+
Begin Until 0.211 (± 0.0%) i/s - 2.000 in 9.469782s
251+
While Loop 0.192 (± 0.0%) i/s - 1.000 in 5.212417s
252+
Kernel loop 0.072 (± 0.0%) i/s - 1.000 in 13.921672s
253+
254+
Comparison:
255+
Begin Until: 0.2 i/s
256+
While Loop: 0.2 i/s - 1.10x slower
257+
Kernel loop: 0.1 i/s - 2.94x slower
258+
```
259+
260+
```
261+
$ ruby -v code/general/loop-vs-while-true-vs-until-false-vs-infinite-range.rb
262+
ruby 3.3.0dev (2023-11-12 master 60e19a0b5f) [x86_64-linux]
263+
264+
Warming up --------------------------------------
265+
While loop 1.000 i/100ms
266+
Until loop 1.000 i/100ms
267+
Kernel loop 1.000 i/100ms
268+
Infinite range 1.000 i/100ms
269+
Calculating -------------------------------------
270+
While loop 0.600 (± 0.0%) i/s - 3.000 in 5.002930s
271+
Until loop 0.594 (± 0.0%) i/s - 3.000 in 5.054004s
272+
Kernel loop 0.258 (± 0.0%) i/s - 2.000 in 7.754873s
273+
Infinite range 0.207 (± 0.0%) i/s - 2.000 in 9.664227s
274+
275+
Comparison:
276+
While loop: 0.6 i/s
277+
Until loop: 0.6 i/s - 1.01x slower
278+
Kernel loop: 0.3 i/s - 2.33x slower
279+
Infinite range: 0.2 i/s - 2.90x slower
280+
```
281+
282+
```
283+
$ ruby -v code/general/loop-vs-while-true.rb
284+
ruby 3.4.10 (2026-06-30 revision 2b0b7728dc) +PRISM [aarch64-linux]
285+
Warming up --------------------------------------
286+
Begin Until 1.000 i/100ms
287+
While Loop 1.000 i/100ms
288+
Until loop 1.000 i/100ms
289+
Kernel loop 1.000 i/100ms
290+
Infinite range 1.000 i/100ms
291+
Calculating -------------------------------------
292+
Begin Until 0.715 (± 0.0%) i/s (1.40 s/i) - 4.000 in 5.593345s
293+
While Loop 0.713 (± 0.0%) i/s (1.40 s/i) - 4.000 in 5.607034s
294+
Until loop 0.710 (± 0.0%) i/s (1.41 s/i) - 4.000 in 5.634041s
295+
Kernel loop 0.387 (± 0.0%) i/s (2.59 s/i) - 2.000 in 5.172241s
296+
Infinite range 0.399 (± 0.0%) i/s (2.50 s/i) - 2.000 in 5.008409s
297+
298+
Comparison:
299+
Begin Until: 0.7 i/s
300+
While Loop: 0.7 i/s - 1.00x slower
301+
Until loop: 0.7 i/s - 1.01x slower
302+
Infinite range: 0.4 i/s - 1.79x slower
303+
Kernel loop: 0.4 i/s - 1.85x slower
304+
```
222305

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

321+
```
322+
$ ruby -v code/general/loop-vs-while-true.rb
323+
ruby 4.0.7 (2026-09-15 revision 229531a6cf) +PRISM [aarch64-linux]
324+
Warming up --------------------------------------
325+
Begin Until 1.000 i/100ms
326+
While Loop 1.000 i/100ms
327+
Until loop 1.000 i/100ms
328+
Kernel loop 1.000 i/100ms
329+
Infinite range 1.000 i/100ms
330+
Calculating -------------------------------------
331+
Begin Until 0.666 (± 0.0%) i/s (1.50 s/i) - 4.000 in 6.006927s
332+
While Loop 0.687 (± 0.0%) i/s (1.46 s/i) - 4.000 in 5.826603s
333+
Until loop 0.686 (± 0.0%) i/s (1.46 s/i) - 4.000 in 5.828591s
334+
Kernel loop 0.520 (± 0.0%) i/s (1.92 s/i) - 3.000 in 5.767842s
335+
Infinite range 0.386 (± 0.0%) i/s (2.59 s/i) - 2.000 in 5.184388s
336+
337+
Comparison:
338+
While Loop: 0.7 i/s
339+
Until loop: 0.7 i/s - 1.00x slower
340+
Begin Until: 0.7 i/s - 1.03x slower
341+
Kernel loop: 0.5 i/s - 1.32x slower
342+
Infinite range: 0.4 i/s - 1.78x slower
343+
```
344+
238345
##### `ancestors.include?` vs `<=` [code](code/general/inheritance-check.rb)
239346

240347
```
@@ -1704,6 +1811,49 @@ improvement.
17041811
> :warning: <br>
17051812
> `Regexp#===` is also faster than `String#match` but you need to switch the order of arguments.
17061813
1814+
```
1815+
$ ruby -v code/string/===-vs-=~-vs-match.rb
1816+
ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-darwin14]
1817+
1818+
Calculating -------------------------------------
1819+
String#=~ 98.184k i/100ms
1820+
Regexp#=== 92.382k i/100ms
1821+
String#match 83.601k i/100ms
1822+
-------------------------------------------------
1823+
String#=~ 2.442M (± 7.6%) i/s - 12.175M
1824+
Regexp#=== 2.259M (± 7.9%) i/s - 11.271M
1825+
String#match 1.840M (± 7.3%) i/s - 9.196M
1826+
1827+
Comparison:
1828+
String#=~: 2442335.1 i/s
1829+
Regexp#===: 2259277.3 i/s - 1.08x slower
1830+
String#match: 1839815.4 i/s - 1.33x slower
1831+
```
1832+
1833+
The next run is from #139, which added `String#include?` to this file; it now has its own entry below.
1834+
1835+
```
1836+
$ ruby -v code/string/===-vs-=~-vs-match.rb
1837+
ruby 2.4.2p198 (2017-09-14 revision 59899) [x86_64-darwin16]
1838+
1839+
Warming up --------------------------------------
1840+
String#include? 238.465k i/100ms
1841+
String#=~ 161.826k i/100ms
1842+
Regexp#=== 147.986k i/100ms
1843+
String#match 136.749k i/100ms
1844+
Calculating -------------------------------------
1845+
String#include? 5.961M (± 8.5%) i/s - 29.808M in 5.035615s
1846+
String#=~ 2.843M (± 8.1%) i/s - 14.241M in 5.040590s
1847+
Regexp#=== 2.689M (± 7.3%) i/s - 13.467M in 5.032779s
1848+
String#match 2.371M (± 5.2%) i/s - 11.897M in 5.031192s
1849+
1850+
Comparison:
1851+
String#include?: 5960785.4 i/s
1852+
String#=~: 2843384.2 i/s - 2.10x slower
1853+
Regexp#===: 2689290.6 i/s - 2.22x slower
1854+
String#match: 2370791.0 i/s - 2.51x slower
1855+
```
1856+
17071857
```
17081858
$ ruby -v code/string/===-vs-=~-vs-match.rb
17091859
ruby 4.0.0 (2025-12-25 revision 553f1675f3) +PRISM [arm64-darwin24]
@@ -1734,6 +1884,46 @@ Comparison:
17341884
See [#59](https://github.com/fastruby/fast-ruby/pull/59) and [#62](https://github.com/fastruby/fast-ruby/pull/62) for discussions.
17351885

17361886

1887+
##### `String#match?` vs `String#include?` vs `String#=~` for a fixed substring [code](code/string/include-vs-match.rb)
1888+
1889+
For a fixed substring, `String#include?` beats a Regexp with `=~`, and `String#match?` (Ruby 2.4+) is as fast or faster.
1890+
1891+
```
1892+
$ ruby -v code/string/include-vs-match.rb
1893+
ruby 3.4.10 (2026-06-30 revision 2b0b7728dc) +PRISM [aarch64-linux]
1894+
Warming up --------------------------------------
1895+
String#match? 1.325M i/100ms
1896+
String#include? 995.530k i/100ms
1897+
String#=~ 853.136k i/100ms
1898+
Calculating -------------------------------------
1899+
String#match? 13.827M (± 3.2%) i/s (72.32 ns/i) - 70.248M in 5.080506s
1900+
String#include? 9.974M (± 1.5%) i/s (100.26 ns/i) - 50.772M in 5.090292s
1901+
String#=~ 8.489M (± 4.8%) i/s (117.81 ns/i) - 42.657M in 5.025211s
1902+
1903+
Comparison:
1904+
String#match?: 13826876.4 i/s
1905+
String#include?: 9974287.2 i/s - 1.39x slower
1906+
String#=~: 8488559.2 i/s - 1.63x slower
1907+
```
1908+
1909+
```
1910+
$ ruby -v code/string/include-vs-match.rb
1911+
ruby 4.0.7 (2026-09-15 revision 229531a6cf) +PRISM [aarch64-linux]
1912+
Warming up --------------------------------------
1913+
String#match? 1.517M i/100ms
1914+
String#include? 1.283M i/100ms
1915+
String#=~ 1.004M i/100ms
1916+
Calculating -------------------------------------
1917+
String#match? 14.967M (± 3.4%) i/s (66.81 ns/i) - 75.870M in 5.069146s
1918+
String#include? 12.845M (± 0.5%) i/s (77.85 ns/i) - 65.441M in 5.094510s
1919+
String#=~ 10.039M (± 1.0%) i/s (99.61 ns/i) - 50.218M in 5.002306s
1920+
1921+
Comparison:
1922+
String#match?: 14967056.6 i/s
1923+
String#include?: 12845487.5 i/s - 1.17x slower
1924+
String#=~: 10038889.7 i/s - 1.49x slower
1925+
```
1926+
17371927
##### `String#gsub` vs `String#sub` vs `String#[]=` [code](code/string/gsub-vs-sub.rb)
17381928

17391929
```

‎code/general/loop-vs-while-true.rb‎

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,31 @@
1-
require "benchmark/ips"
1+
require 'benchmark/ips'
22

33
NUMBER = 100_000_000
44

5-
def fast
5+
# The three loops that test a condition tie on Ruby 4.0.
6+
def fastest
7+
index = 0
8+
begin
9+
index += 1
10+
end until index > NUMBER
11+
end
12+
13+
def faster
614
index = 0
715
while true
816
break if index > NUMBER
917
index += 1
1018
end
1119
end
1220

21+
def fast
22+
index = 0
23+
until false
24+
break if index > NUMBER
25+
index += 1
26+
end
27+
end
28+
1329
def slow
1430
index = 0
1531
loop do
@@ -18,8 +34,22 @@ def slow
1834
end
1935
end
2036

37+
# Endless ranges are Ruby 2.6+ syntax, so the method is defined from a string, which older Rubies never parse.
38+
if RUBY_VERSION >= '2.6.0'
39+
eval <<-RUBY
40+
def slower
41+
(0..).each do |index|
42+
break if index > NUMBER
43+
end
44+
end
45+
RUBY
46+
end
47+
2148
Benchmark.ips do |x|
22-
x.report("While Loop") { fast }
23-
x.report("Kernel loop") { slow }
49+
x.report('Begin Until') { fastest }
50+
x.report('While Loop') { faster }
51+
x.report('Until loop') { fast }
52+
x.report('Kernel loop') { slow }
53+
x.report('Infinite range') { slower } if RUBY_VERSION >= '2.6.0'
2454
x.compare!
2555
end

‎code/string/include-vs-match.rb‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
require 'benchmark/ips'
2+
3+
# For a fixed substring, String#include? beats a Regexp with =~.
4+
# String#match? (Ruby 2.4+) is as fast as include? or faster on current CRuby.
5+
def faster
6+
'foo'.freeze.match?(/boo/)
7+
end
8+
9+
def fast
10+
'foo'.freeze.include?('boo')
11+
end
12+
13+
def slow
14+
'foo'.freeze =~ /boo/
15+
end
16+
17+
Benchmark.ips do |x|
18+
x.report('String#match?') { faster } if RUBY_VERSION >= '2.4.0'
19+
x.report('String#include?') { fast }
20+
x.report('String#=~') { slow }
21+
x.compare!
22+
end

0 commit comments

Comments
 (0)