- Notifications
You must be signed in to change notification settings - Fork379
💨 Writing Fast Ruby 😍 -- Collect Common Ruby idioms.
fastruby/fast-ruby
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
InErik Michaels-Ober's great talk, 'Writing Fast Ruby':Video @ Baruco 2014,Slide, he presented us with many idioms that lead to faster running Ruby code. He inspired me to document these to let more people know. I try to link to real commits so people can see that this can really have benefits in the real world.This does not mean you can always blindly replace one with another. It depends on the context (e.g.gsub
versustr
). Friendly reminder: Use with caution!
Each idiom has a corresponding code example that resides incode.
All results listed in README.md are running with Ruby 2.2.0p0 on OS X 10.10.1. Machine information: MacBook Pro (Retina, 15-inch, Mid 2014), 2.5 GHz Intel Core i7, 16 GB 1600 MHz DDR3. Your results may vary, but you get the idea. : )
You can checkoutthe GitHub Actions build for these benchmark results ran against different Ruby implementations.
Let's write faster code, together! <3
Checkout thefasterer project - it's a static analysis that checks speed idioms written in this repo.
Usebenchmark-ips (2.0+).
require"benchmark/ips"deffastenddefslowendBenchmark.ipsdo |x|x.report("fast code description"){fast}x.report("slow code description"){slow}x.compare!end
attr_accessor
vsgetter and setter
code
https://www.omniref.com/ruby/2.2.0/files/method.h?#annotation=4081781&line=47
$ ruby -v code/general/attr-accessor-vs-getter-and-setter.rbruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-darwin14]Calculating ------------------------------------- getter_and_setter 61.240k i/100ms attr_accessor 66.535k i/100ms------------------------------------------------- getter_and_setter 1.660M (± 9.7%) i/s - 8.267M attr_accessor 1.865M (± 9.2%) i/s - 9.248MComparison: attr_accessor: 1865408.4 i/s getter_and_setter: 1660021.9 i/s - 1.12x slower
begin...rescue
vsrespond_to?
for Control Flowcode
$ ruby -v code/general/begin-rescue-vs-respond-to.rbruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-darwin14]Calculating ------------------------------------- begin...rescue 29.452k i/100ms respond_to? 106.528k i/100ms------------------------------------------------- begin...rescue 371.591k (± 5.4%) i/s - 1.855M respond_to? 3.277M (± 7.5%) i/s - 16.299MComparison: respond_to?: 3276972.3 i/s begin...rescue: 371591.0 i/s - 8.82x slower
define_method
vsmodule_eval
for Defining Methodscode
$ ruby -v code/general/define_method-vs-module-eval.rbruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-darwin14]Calculating -------------------------------------module_eval with string 125.000 i/100ms define_method 138.000 i/100ms-------------------------------------------------module_eval with string 1.130k (±20.3%) i/s - 5.500k define_method 1.346k (±25.9%) i/s - 6.348kComparison: define_method: 1345.6 i/smodule_eval with string: 1129.7 i/s - 1.19x slower
String#constantize
vs a comparison for inflectioncode
ActiveSupport'sString#constantize "resolves the constant reference expression in its receiver".
ruby 2.7.3p183 (2021-04-05 revision 6847ee089d) [x86_64-darwin20]Calculating -------------------------------------using an if statement 8.124M (± 1.8%) i/s - 41.357M in 5.092437s String#constantize 2.462M (± 2.4%) i/s - 12.315M in 5.004089sComparison:using an if statement: 8123851.3 i/s String#constantize: 2462371.2 i/s - 3.30x (± 0.00) slower
raise
vsE2MM#Raise
for raising (and defining) exceptionscode
Ruby'sException2MessageMapper module allows one to define and raise exceptions with predefined messages.
$ ruby -v code/general/raise-vs-e2mmap.rbruby 2.2.3p173 (2015-08-18 revision 51636) [x86_64-darwin14]Calculating -------------------------------------Ruby exception: E2MM#Raise 2.865k i/100msRuby exception: Kernel#raise 42.215k i/100ms-------------------------------------------------Ruby exception: E2MM#Raise 27.270k (± 8.8%) i/s - 137.520kRuby exception: Kernel#raise 617.446k (± 7.9%) i/s - 3.082MComparison:Ruby exception: Kernel#raise: 617446.2 i/sRuby exception: E2MM#Raise: 27269.8 i/s - 22.64x slowerCalculating -------------------------------------Custom exception: E2MM#Raise 2.807k i/100msCustom exception: Kernel#raise 45.313k i/100ms-------------------------------------------------Custom exception: E2MM#Raise 29.005k (± 7.2%) i/s - 145.964kCustom exception: Kernel#raise 589.149k (± 7.8%) i/s - 2.945MComparison:Custom exception: Kernel#raise: 589148.7 i/sCustom exception: E2MM#Raise: 29004.8 i/s - 20.31x slower
loop
vswhile true
code
$ ruby -v code/general/loop-vs-while-true.rbruby 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.982355sComparison: While Loop: 0.5 i/s Kernel loop: 0.2 i/s - 2.41x slower
ancestors.include?
vs<=
code
$ ruby -vW0 code/general/inheritance-check.rbruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-linux]Warming up -------------------------------------- less than or equal 66.992k i/100ms ancestors.include? 16.943k i/100msCalculating ------------------------------------- less than or equal 1.250M (± 6.4%) i/s - 6.230M in 5.006896s ancestors.include? 192.603k (± 4.8%) i/s - 965.751k in 5.025917sComparison: less than or equal: 1249606.0 i/s ancestors.include?: 192602.9 i/s - 6.49x slower
call
vssend
vsmethod_missing
code
$ ruby -v code/method/call-vs-send-vs-method_missing.rbruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-darwin14]Calculating ------------------------------------- call 115.094k i/100ms send 105.258k i/100ms method_missing 100.762k i/100ms------------------------------------------------- call 3.811M (± 5.9%) i/s - 18.991M send 3.244M (± 7.2%) i/s - 16.210M method_missing 2.729M (± 9.8%) i/s - 13.401MComparison: call: 3811183.4 i/s send: 3244239.1 i/s - 1.17x slower method_missing: 2728893.0 i/s - 1.40x slower
Normal way to apply method vs&method(...)
code
$ ruby -v code/general/block-apply-method.rbruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-darwin14]Calculating ------------------------------------- normal 85.749k i/100ms &method 35.529k i/100ms------------------------------------------------- normal 1.867M (± 7.6%) i/s - 9.347M &method 467.095k (± 6.4%) i/s - 2.345MComparison: normal: 1866669.5 i/s &method: 467095.4 i/s - 4.00x slower
Function with single Array argument vs splat argumentscode
$ ruby -v code/general/array-argument-vs-splat-arguments.rbruby 2.1.7p400 (2015-08-18 revision 51632) [x86_64-linux-gnu]Calculating -------------------------------------Function with single Array argument 157.231k i/100msFunction with splat arguments 4.983k i/100ms-------------------------------------------------Function with single Array argument 5.581M (± 2.0%) i/s - 27.987MFunction with splat arguments 54.428k (± 3.3%) i/s - 274.065kComparison:Function with single Array argument: 5580972.6 i/sFunction with splat arguments: 54427.7 i/s - 102.54x slower
Hash vs OpenStruct on access assuming you already have a Hash or an OpenStructcode
$ ruby -v code/general/hash-vs-openstruct-on-access.rbruby 2.2.3p173 (2015-08-18 revision 51636) [x86_64-darwin14]Calculating ------------------------------------- Hash 128.344k i/100ms OpenStruct 110.723k i/100ms------------------------------------------------- Hash 5.279M (± 7.0%) i/s - 26.311M OpenStruct 3.048M (± 7.0%) i/s - 15.169MComparison: Hash: 5278844.0 i/s OpenStruct: 3048139.8 i/s - 1.73x slower
Hash vs OpenStruct (creation)code
$ ruby -v code/general/hash-vs-openstruct.rbruby 2.2.3p173 (2015-08-18 revision 51636) [x86_64-darwin14]Calculating ------------------------------------- Hash 75.510k i/100ms OpenStruct 9.126k i/100ms------------------------------------------------- Hash 1.604M (±11.0%) i/s - 7.929M OpenStruct 96.855k (± 9.9%) i/s - 483.678kComparison: Hash: 1604259.1 i/s OpenStruct: 96855.3 i/s - 16.56x slower
Kernel#format vs Float#round().to_scode
$ ruby -v code/general/format-vs-round-and-to-s.rbruby 2.3.3p222 (2016-11-21 revision 56859) [x86_64-darwin15]Warming up -------------------------------------- Float#round 106.645k i/100ms Kernel#format 84.304k i/100ms String#% 78.635k i/100msCalculating ------------------------------------- Float#round 1.570M (± 3.2%) i/s - 7.892M in 5.030672s Kernel#format 1.144M (± 3.0%) i/s - 5.733M in 5.015621s String#% 1.047M (± 4.2%) i/s - 5.269M in 5.042970sComparison: Float#round: 1570411.4 i/s Kernel#format: 1144036.6 i/s - 1.37x slower String#%: 1046689.1 i/s - 1.50x slower
Array#bsearch
vsArray#find
code
WARNING:bsearch
ONLY works onsorted array. More details please see#29.
$ ruby -v code/array/bsearch-vs-find.rbruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-darwin14]Calculating ------------------------------------- find 1.000 i/100ms bsearch 42.216k i/100ms------------------------------------------------- find 0.184 (± 0.0%) i/s - 1.000 in 5.434758s bsearch 577.301k (± 6.6%) i/s - 2.913MComparison: bsearch: 577300.7 i/s find: 0.2 i/s - 3137489.63x slower
Array#length
vsArray#size
vsArray#count
code
Use#length
when you only want to know how many elements in the array,#count
could also achieve this. However#count
should be use for counting specific elements in array.Note#size
is an alias of#length
.
$ ruby -v code/array/length-vs-size-vs-count.rbruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-darwin14]Calculating ------------------------------------- Array#length 172.998k i/100ms Array#size 168.130k i/100ms Array#count 164.911k i/100ms------------------------------------------------- Array#length 11.394M (± 6.1%) i/s - 56.743M Array#size 11.303M (± 6.5%) i/s - 56.324M Array#count 9.195M (± 8.6%) i/s - 45.680MComparison: Array#length: 11394036.7 i/s Array#size: 11302701.1 i/s - 1.01x slower Array#count: 9194976.2 i/s - 1.24x slower
Array#shuffle.first
vsArray#sample
code
Array#shuffle
allocates an extra array.Array#sample
indexes into the array without allocating an extra array.
This is the reason why Array#sample exists.
—— @sferikrails/rails#17245
$ ruby -v code/array/shuffle-first-vs-sample.rbruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-darwin14]Calculating ------------------------------------- Array#shuffle.first 25.406k i/100ms Array#sample 125.101k i/100ms------------------------------------------------- Array#shuffle.first 304.341k (± 4.3%) i/s - 1.524M Array#sample 5.727M (± 8.6%) i/s - 28.523MComparison: Array#sample: 5727032.0 i/s Array#shuffle.first: 304341.1 i/s - 18.82x slower
Array#[](0)
vsArray#first
code
$ ruby -v code/array/array-first-vs-index.rbruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-darwin14]Calculating ------------------------------------- Array#[0] 152.751k i/100ms Array#first 148.088k i/100ms------------------------------------------------- Array#[0] 8.614M (± 7.0%) i/s - 42.923M Array#first 7.465M (±10.7%) i/s - 36.874MComparison: Array#[0]: 8613583.7 i/s Array#first: 7464526.6 i/s - 1.15x slower
Array#[](-1)
vsArray#last
code
$ ruby -v code/array/array-last-vs-index.rbruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-darwin14]Calculating ------------------------------------- Array#[-1] 151.940k i/100ms Array#last 153.371k i/100ms------------------------------------------------- Array#[-1] 8.582M (± 4.6%) i/s - 42.847M Array#last 7.639M (± 5.7%) i/s - 38.189MComparison: Array#[-1]: 8582074.3 i/s Array#last: 7639254.5 i/s - 1.12x slower
Array#insert
vsArray#unshift
code
$ ruby -v code/array/insert-vs-unshift.rbruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-darwin10.0]Calculating ------------------------------------- Array#unshift 4.000 i/100ms Array#insert 1.000 i/100ms------------------------------------------------- Array#unshift 44.947 (± 6.7%) i/s - 224.000 Array#insert 0.171 (± 0.0%) i/s - 1.000 in 5.841595sComparison: Array#unshift: 44.9 i/s Array#insert: 0.2 i/s - 262.56x slower
Array#concat
vsArray#+
code
Array#+
returns a new array built by concatenating the two arrays together toproduce a third array.Array#concat
appends the elements of the other array to self.This means that the + operator will create a new array each time it is called(which is expensive), while concat only appends the new element.
$ ruby -v code/array/array-concat-vs-+.rbruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin18]Warming up -------------------------------------- Array#concat 23.000 i/100ms Array#+ 1.000 i/100msCalculating ------------------------------------- Array#concat 217.669 (±15.2%) i/s - 1.058k in 5.016952s Array#+ 1.475 (± 0.0%) i/s - 8.000 in 5.467642sComparison: Array#concat: 217.7 i/s Array#+: 1.5 i/s - 147.54x slower
Array#new
vsFixnum#times + map
code
Typical slowdown is 40-60% depending on the size of the array. See the correspondingpull request for performance characteristics.
ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-darwin15]Calculating ------------------------------------- Array#new 63.875k i/100ms Fixnum#times + map 48.010k i/100ms------------------------------------------------- Array#new 1.070M (± 2.2%) i/s - 5.365M Fixnum#times + map 678.097k (± 2.7%) i/s - 3.409MComparison: Array#new: 1069837.0 i/s Fixnum#times + map: 678097.4 i/s - 1.58x slower
Array#sort.reverse
vsArray#sort_by
+ blockcode
$ ruby -v code/array/sort-reverse-vs-sort_by.rbruby 2.5.2p104 (2018-10-18 revision 65133) [x86_64-darwin13]Warming up --------------------------------------Array#sort.reverse 16.231k i/100msArray#sort_by &:-@ 5.406k i/100msCalculating -------------------------------------Array#sort.reverse 149.492k (±11.0%) i/s - 746.626k in 5.070375sArray#sort_by &:-@ 51.981k (± 8.8%) i/s - 259.488k in 5.041625sComparison:Array#sort.reverse: 149492.2 i/sArray#sort_by &:-@: 51980.6 i/s - 2.88x (± 0.00) slower
Enumerable#each + push
vsEnumerable#map
code
$ ruby -v code/enumerable/each-push-vs-map.rbruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-darwin14]Calculating ------------------------------------- Array#each + push 9.025k i/100ms Array#map 13.947k i/100ms------------------------------------------------- Array#each + push 99.634k (± 3.2%) i/s - 505.400k Array#map 158.091k (± 4.2%) i/s - 794.979kComparison: Array#map: 158090.9 i/s Array#each + push: 99634.2 i/s - 1.59x slower
Enumerable#each
vsfor
loopcode
$ ruby -v code/enumerable/each-vs-for-loop.rbruby 2.2.0preview1 (2014-09-17 trunk 47616) [x86_64-darwin14]Calculating ------------------------------------- For loop 17.111k i/100ms #each 18.464k i/100ms------------------------------------------------- For loop 198.517k (± 5.3%) i/s - 992.438k #each 208.157k (± 5.0%) i/s - 1.052MComparison: #each: 208157.4 i/s For loop: 198517.3 i/s - 1.05x slower
Enumerable#each_with_index
vswhile
loopcode
$ ruby -v code/enumerable/each_with_index-vs-while-loop.rbruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-darwin14]Calculating ------------------------------------- While Loop 22.553k i/100ms each_with_index 11.963k i/100ms------------------------------------------------- While Loop 240.752k (± 7.1%) i/s - 1.218M each_with_index 126.753k (± 5.9%) i/s - 634.039kComparison: While Loop: 240752.1 i/s each_with_index: 126753.4 i/s - 1.90x slower
Enumerable#map
...Array#flatten
vsEnumerable#flat_map
code
-- @sferikrails/rails@3413b88,Replace map.flatten with flat_map,Replace map.flatten(1) with flat_map
$ ruby -v code/enumerable/map-flatten-vs-flat_map.rbruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-darwin14]Calculating -------------------------------------Array#map.flatten(1) 3.315k i/100ms Array#map.flatten 3.283k i/100ms Array#flat_map 5.350k i/100ms-------------------------------------------------Array#map.flatten(1) 33.801k (± 4.3%) i/s - 169.065k Array#map.flatten 34.530k (± 6.0%) i/s - 173.999k Array#flat_map 55.980k (± 5.0%) i/s - 283.550kComparison: Array#flat_map: 55979.6 i/s Array#map.flatten: 34529.6 i/s - 1.62x slowerArray#map.flatten(1): 33800.6 i/s - 1.66x slower
Enumerable#reverse.each
vsEnumerable#reverse_each
code
Enumerable#reverse
allocates an extra array.Enumerable#reverse_each
yields each value without allocating an extra array.
This is the reason whyEnumerable#reverse_each
exists.
-- @sferikrails/rails#17244
$ ruby -v code/enumerable/reverse-each-vs-reverse_each.rbruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-darwin14]Calculating ------------------------------------- Array#reverse.each 16.746k i/100ms Array#reverse_each 18.590k i/100ms------------------------------------------------- Array#reverse.each 190.729k (± 4.8%) i/s - 954.522k Array#reverse_each 216.060k (± 4.3%) i/s - 1.078MComparison: Array#reverse_each: 216060.5 i/s Array#reverse.each: 190729.1 i/s - 1.13x slower
Enumerable#sort_by.first
vsEnumerable#min_by
code
Enumerable#sort_by
performs a sort of the enumerable and allocates anew array the size of the enumerable.Enumerable#min_by
doesn'tperform a sort or allocate an array the size of the enumerable.Similar comparisons hold forEnumerable#sort_by.last
vsEnumerable#max_by
,Enumerable#sort.first
vsEnumerable#min
, andEnumerable#sort.last
vsEnumerable#max
.
$ ruby -v code/enumerable/sort_by-first-vs-min_by.rbruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin17]Warming up -------------------------------------- Enumerable#min_by 15.170k i/100msEnumerable#sort_by...first 10.413k i/100msCalculating ------------------------------------- Enumerable#min_by 157.877k (± 0.9%) i/s - 804.010k in 5.093048sEnumerable#sort_by...first 106.831k (± 1.3%) i/s - 541.476k in 5.069403sComparison: Enumerable#min_by: 157877.0 i/sEnumerable#sort_by...first: 106831.1 i/s - 1.48x slower
Enumerable#detect
vsEnumerable#select.first
code
$ ruby -v code/enumerable/select-first-vs-detect.rbruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-darwin14]Calculating -------------------------------------Enumerable#select.first 8.515k i/100ms Enumerable#detect 33.885k i/100ms-------------------------------------------------Enumerable#select.first 89.757k (± 5.0%) i/s - 1.797M Enumerable#detect 434.304k (± 5.2%) i/s - 8.675MComparison: Enumerable#detect: 434304.2 i/sEnumerable#select.first: 89757.4 i/s - 4.84x slower
Enumerable#select.last
vsEnumerable#reverse.detect
code
$ ruby -v code/enumerable/select-last-vs-reverse-detect.rbruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-darwin14]Calculating -------------------------------------Enumerable#reverse.detect 62.636k i/100msEnumerable#select.last 11.687k i/100ms-------------------------------------------------Enumerable#reverse.detect 1.263M (± 8.2%) i/s - 6.326MEnumerable#select.last 119.387k (± 5.7%) i/s - 596.037kComparison:Enumerable#reverse.detect: 1263100.2 i/sEnumerable#select.last: 119386.8 i/s - 10.58x slower
Enumerable#sort
vsEnumerable#sort_by
code
$ ruby -v code/enumerable/sort-vs-sort_by.rbruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-darwin14]Calculating -------------------------------------Enumerable#sort_by (Symbol#to_proc) 2.680k i/100ms Enumerable#sort_by 2.462k i/100ms Enumerable#sort 1.320k i/100ms-------------------------------------------------Enumerable#sort_by (Symbol#to_proc) 25.916k (± 4.4%) i/s - 131.320k Enumerable#sort_by 24.650k (± 5.1%) i/s - 125.562k Enumerable#sort 14.018k (± 5.6%) i/s - 69.960kComparison:Enumerable#sort_by (Symbol#to_proc): 25916.1 i/s Enumerable#sort_by: 24650.2 i/s - 1.05x slower Enumerable#sort: 14018.3 i/s - 1.85x slower
Enumerable#inject Symbol
vsEnumerable#inject Proc
code
Of note,to_proc
for 1.8.7 is considerable slower than the block format
$ ruby -v code/enumerable/inject-symbol-vs-block.rbruby 2.2.4p230 (2015-12-16 revision 53155) [x86_64-darwin14]Warming up -------------------------------------- inject symbol 1.893k i/100ms inject to_proc 1.583k i/100ms inject block 1.390k i/100msCalculating ------------------------------------- inject symbol 19.001k (± 3.8%) i/s - 96.543k inject to_proc 15.958k (± 3.5%) i/s - 80.733k inject block 14.063k (± 3.9%) i/s - 70.890kComparison: inject symbol: 19001.5 i/s inject to_proc: 15958.3 i/s - 1.19x slower inject block: 14063.1 i/s - 1.35x slower
Date.iso8601
vsDate.parse
code
When expecting well-formatted data from e.g. an API,iso8601
is faster and will raise anArgumentError
on malformed input.
$ ruby -v code/date/iso8601-vs-parse.rbruby 2.4.3p205 (2017-12-14 revision 61247) [x86_64-darwin17]Warming up -------------------------------------- Date.iso8601 28.880k i/100ms Date.parse 15.805k i/100msCalculating ------------------------------------- Date.iso8601 328.035k (± 4.7%) i/s - 1.646M in 5.029287s Date.parse 175.546k (± 3.8%) i/s - 885.080k in 5.049444sComparison: Date.iso8601: 328035.3 i/s Date.parse: 175545.9 i/s - 1.87x slower
Hash#[]
vsHash#fetch
code
If you use Ruby 2.2,Symbol
could be more performant thanString
asHash
keys.Read more regarding this:Symbol GC in Ruby 2.2 andUnraveling String Key Performance in Ruby 2.2.
$ ruby -v code/hash/bracket-vs-fetch.rbruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-darwin14]Calculating ------------------------------------- Hash#[], symbol 143.850k i/100ms Hash#fetch, symbol 137.425k i/100ms Hash#[], string 143.083k i/100ms Hash#fetch, string 120.417k i/100ms------------------------------------------------- Hash#[], symbol 7.531M (± 6.6%) i/s - 37.545M Hash#fetch, symbol 6.644M (± 8.2%) i/s - 32.982M Hash#[], string 6.657M (± 7.7%) i/s - 33.195M Hash#fetch, string 3.981M (± 8.7%) i/s - 19.748MComparison: Hash#[], symbol: 7531355.8 i/s Hash#[], string: 6656818.8 i/s - 1.13x slower Hash#fetch, symbol: 6643665.5 i/s - 1.13x slower Hash#fetch, string: 3981166.5 i/s - 1.89x slower
Hash#dig
vsHash#[]
vsHash#fetch
code
Ruby 2.3 introducedHash#dig
which is a readableand performant option for retrieval from a nested hash, returningnil
if an extraction step fails.See#102 (comment) for more info.
$ ruby -v code/hash/dig-vs-\[\]-vs-fetch.rbruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-darwin15]Calculating ------------------------------------- Hash#dig 5.719M (± 6.1%) i/s - 28.573M in 5.013997s Hash#[] 6.066M (± 6.9%) i/s - 30.324M in 5.025614s Hash#[] || 5.366M (± 6.5%) i/s - 26.933M in 5.041403s Hash#[] && 2.782M (± 4.8%) i/s - 13.905M in 5.010328s Hash#fetch 4.101M (± 6.1%) i/s - 20.531M in 5.024945s Hash#fetch fallback 2.975M (± 5.5%) i/s - 14.972M in 5.048880sComparison: Hash#[]: 6065791.0 i/s Hash#dig: 5719290.9 i/s - same-ish: difference falls within error Hash#[] ||: 5366226.5 i/s - same-ish: difference falls within error Hash#fetch: 4101102.1 i/s - 1.48x slower Hash#fetch fallback: 2974906.9 i/s - 2.04x slower Hash#[] &&: 2781646.6 i/s - 2.18x slower
Hash[]
vsHash#dup
code
Source:http://tenderlovemaking.com/2015/02/11/weird-stuff-with-hashes.html
Does this mean that you should switch to Hash[]?Only if your benchmarks can prove that it’s a bottleneck.Please please please don’t change all of your code becausethis shows it’s faster. Make sure to measure your app performance first.
$ ruby -v code/hash/bracket-vs-dup.rbruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-darwin14]Calculating ------------------------------------- Hash[] 29.403k i/100ms Hash#dup 16.195k i/100ms------------------------------------------------- Hash[] 343.987k (± 8.7%) i/s - 1.735M Hash#dup 163.516k (±10.2%) i/s - 825.945kComparison: Hash[]: 343986.5 i/s Hash#dup: 163516.3 i/s - 2.10x slower
Hash#fetch
with argument vsHash#fetch
+ blockcode
Note that the speedup in the block version comes from avoiding repeated
construction of the argument. If the argument is a constant, number symbol or
something of that sort the argument version is actually slightly faster
See also#39 (comment)
$ ruby -v code/hash/fetch-vs-fetch-with-block.rbruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-darwin13]Calculating ------------------------------------- Hash#fetch + const 129.868k i/100ms Hash#fetch + block 125.254k i/100ms Hash#fetch + arg 121.155k i/100ms------------------------------------------------- Hash#fetch + const 7.031M (± 7.0%) i/s - 34.934M Hash#fetch + block 6.815M (± 4.2%) i/s - 34.069M Hash#fetch + arg 4.753M (± 5.6%) i/s - 23.746MComparison: Hash#fetch + const: 7030600.4 i/s Hash#fetch + block: 6814826.7 i/s - 1.03x slower Hash#fetch + arg: 4752567.2 i/s - 1.48x slower
Hash#each_key
instead ofHash#keys.each
code
Hash#keys.each
allocates an array of keys;Hash#each_key
iterates through the keys without allocating a new array.
This is the reason whyHash#each_key
exists.
—— @sferikrails/rails#17099
$ ruby -v code/hash/keys-each-vs-each_key.rbruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-darwin14]Calculating ------------------------------------- Hash#keys.each 56.690k i/100ms Hash#each_key 59.658k i/100ms------------------------------------------------- Hash#keys.each 869.262k (± 5.0%) i/s - 4.365M Hash#each_key 1.049M (± 6.0%) i/s - 5.250MComparison: Hash#each_key: 1049161.6 i/s Hash#keys.each: 869262.3 i/s - 1.21x slower
Hash#key?
instead ofHash#keys.include?
code
Hash#keys.include?
allocates an array of keys and performs an O(n) search;Hash#key?
performs an O(1) hash lookup without allocating a new array.
$ ruby -v code/hash/keys-include-vs-key.rbruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin17]Calculating ------------------------------------- Hash#keys.include? 8.612k (± 2.5%) i/s - 43.248k in 5.024749s Hash#key? 6.366M (± 5.5%) i/s - 31.715M in 5.002276sComparison: Hash#key?: 6365855.5 i/s Hash#keys.include?: 8612.4 i/s - 739.15x slower
Hash#value?
instead ofHash#values.include?
code
Hash#values.include?
allocates an array of values and performs an O(n) search;Hash#value?
performs an O(n) search without allocating a new array.
$ ruby -v code/hash/values-include-vs-value.rbruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin17]Calculating -------------------------------------Hash#values.include? 23.187k (± 4.3%) i/s - 117.720k in 5.086976s Hash#value? 38.395k (± 1.0%) i/s - 194.361k in 5.062696sComparison: Hash#value?: 38395.0 i/sHash#values.include?: 23186.8 i/s - 1.66x slower
Hash#merge!
vsHash#[]=
code
$ ruby -v code/hash/merge-bang-vs-\[\]=.rbruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-darwin14]Calculating ------------------------------------- Hash#merge! 1.023k i/100ms Hash#[]= 2.844k i/100ms------------------------------------------------- Hash#merge! 10.653k (± 4.9%) i/s - 53.196k Hash#[]= 28.287k (±12.4%) i/s - 142.200kComparison: Hash#[]=: 28287.1 i/s Hash#merge!: 10653.3 i/s - 2.66x slower
Hash#update
vsHash#[]=
code
$ ruby -v code/hash/update-vs-\[\]=.rbruby 2.6.6p146 (2020-03-31 revision 67876) [x86_64-darwin18]Warming up -------------------------------------- Hash#[]= 7.453k i/100ms Hash#update 4.311k i/100msCalculating ------------------------------------- Hash#[]= 74.764k (± 1.9%) i/s - 380.103k in 5.085962s Hash#update 43.220k (± 0.8%) i/s - 219.861k in 5.087364sComparison: Hash#[]=: 74764.0 i/s Hash#update: 43220.1 i/s - 1.73x (± 0.00) slower
Hash#merge
vsHash#**other
code
$ ruby -v code/hash/merge-vs-double-splat-operator.rbruby 2.3.3p222 (2016-11-21 revision 56859) [x86_64-darwin15]Warming up -------------------------------------- Hash#**other 64.624k i/100ms Hash#merge 38.827k i/100msCalculating ------------------------------------- Hash#**other 798.397k (± 6.9%) i/s - 4.007M in 5.053516s Hash#merge 434.171k (± 4.5%) i/s - 2.174M in 5.018927sComparison: Hash#**other: 798396.6 i/s Hash#merge: 434170.8 i/s - 1.84x slower
Hash#merge
vsHash#merge!
code
$ ruby -v code/hash/merge-vs-merge-bang.rbruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-darwin14]Calculating ------------------------------------- Hash#merge 39.000 i/100ms Hash#merge! 1.008k i/100ms------------------------------------------------- Hash#merge 409.610 (± 7.6%) i/s - 2.067k Hash#merge! 9.830k (± 5.8%) i/s - 49.392kComparison: Hash#merge!: 9830.3 i/s Hash#merge: 409.6 i/s - 24.00x slower
{}#merge!(Hash)
vsHash#merge({})
vsHash#dup#merge!({})
code
When we don't want to modify the original hash, and we want duplicates to be created
See#42 for more details.
$ ruby -v code/hash/merge-bang-vs-merge-vs-dup-merge-bang.rbruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-linux]Calculating -------------------------------------{}#merge!(Hash) do end 2.006k i/100ms Hash#merge({}) 762.000 i/100ms Hash#dup#merge!({}) 736.000 i/100ms-------------------------------------------------{}#merge!(Hash) do end 20.055k (± 2.0%) i/s - 100.300k in 5.003322s Hash#merge({}) 7.676k (± 1.2%) i/s - 38.862k in 5.063382s Hash#dup#merge!({}) 7.440k (± 1.1%) i/s - 37.536k in 5.045851sComparison:{}#merge!(Hash) do end: 20054.8 i/s Hash#merge({}): 7676.3 i/s - 2.61x slower Hash#dup#merge!({}): 7439.9 i/s - 2.70x slower
Hash#sort_by
vsHash#sort
code
To sort hash by key.
$ ruby -v code/hash/hash-key-sort_by-vs-sort.rbruby 2.2.1p85 (2015-02-26 revision 49769) [x86_64-darwin14]Calculating ------------------------------------- sort_by + to_h 11.468k i/100ms sort + to_h 8.107k i/100ms------------------------------------------------- sort_by + to_h 122.176k (± 6.0%) i/s - 619.272k sort + to_h 81.973k (± 4.7%) i/s - 413.457kComparison: sort_by + to_h: 122176.2 i/s sort + to_h: 81972.8 i/s - 1.49x slower
NativeHash#slice
vs other slice implementations before nativecode
Since ruby 2.5, Hash comes with aslice
method to select hash members by keys.
$ ruby -v code/hash/slice-native-vs-before-native.rbruby 2.5.3p105 (2018-10-18 revision 65156) [x86_64-linux]Warming up --------------------------------------Hash#native-slice 178.077k i/100msArray#each 124.311k i/100msArray#each_w/_object 110.818k i/100msHash#select-include 66.972k i/100msCalculating -------------------------------------Hash#native-slice 2.540M (± 1.5%) i/s - 12.822M in 5.049955sArray#each 1.614M (± 1.0%) i/s - 8.080M in 5.007925sArray#each_w/_object 1.353M (± 2.6%) i/s - 6.760M in 5.000441sHash#select-include 760.944k (± 0.9%) i/s - 3.817M in 5.017123sComparison:Hash#native-slice : 2539515.5 i/sArray#each : 1613665.5 i/s - 1.57x slowerArray#each_w/_object: 1352851.8 i/s - 1.88x slowerHash#select-include : 760944.2 i/s - 3.34x slower
Block vsSymbol#to_proc
code
Symbol#to_proc
is considerably more concise than using block syntax.
...In some cases, it reduces the number of lines of code.
—— @sferikrails/rails#16833
$ ruby -v code/proc-and-block/block-vs-to_proc.rbruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-darwin14]Calculating ------------------------------------- Block 4.632k i/100ms Symbol#to_proc 5.225k i/100ms------------------------------------------------- Block 47.914k (± 6.3%) i/s - 240.864k Symbol#to_proc 54.791k (± 4.1%) i/s - 276.925kComparison: Symbol#to_proc: 54791.1 i/s Block: 47914.3 i/s - 1.14x slower
Proc#call
and block arguments vsyield
code
In MRI Ruby before 2.5, block argumentsare converted to Procs, which incurs a heap allocation.
$ ruby -v code/proc-and-block/proc-call-vs-yield.rbruby 2.4.4p296 (2018-03-28 revision 63013) [x86_64-darwin18]Calculating ------------------------------------- block.call 1.967M (± 2.0%) i/s - 9.871M in 5.019328s block + yield 2.147M (± 3.3%) i/s - 10.814M in 5.044319s unused block 2.265M (± 1.9%) i/s - 11.333M in 5.004522s yield 10.436M (± 1.6%) i/s - 52.260M in 5.008851sComparison: yield: 10436414.0 i/s unused block: 2265399.0 i/s - 4.61x slower block + yield: 2146619.0 i/s - 4.86x slower block.call: 1967300.9 i/s - 5.30x slower
MRI Ruby 2.5 implementsLazy Proc allocation for block parameters, which speeds things up by about 3x.:
$ ruby -v code/proc-and-block/proc-call-vs-yield.rbruby 2.5.3p105 (2018-10-18 revision 65156) [x86_64-darwin18]Calculating ------------------------------------- block.call 1.970M (± 2.3%) i/s - 9.863M in 5.009599s block + yield 9.075M (± 2.6%) i/s - 45.510M in 5.018369s unused block 11.176M (± 2.7%) i/s - 55.977M in 5.012741s yield 10.588M (± 1.9%) i/s - 53.108M in 5.017755sComparison: unused block: 11176355.0 i/s yield: 10588342.3 i/s - 1.06x slower block + yield: 9075355.5 i/s - 1.23x slower block.call: 1969834.0 i/s - 5.67x slower
MRI Ruby 2.6 implementsan optimization for block.call where a block parameter is passed:
$ ruby -v code/proc-and-block/proc-call-vs-yield.rbruby 2.6.1p33 (2019-01-30 revision 66950) [x86_64-darwin18]Calculating ------------------------------------- block.call 10.587M (± 1.2%) i/s - 52.969M in 5.003808s block + yield 12.630M (± 0.3%) i/s - 63.415M in 5.020910s unused block 15.981M (± 0.8%) i/s - 80.255M in 5.022305s yield 15.352M (± 3.1%) i/s - 76.816M in 5.009404sComparison: unused block: 15980789.4 i/s yield: 15351931.0 i/s - 1.04x slower block + yield: 12630378.1 i/s - 1.27x slower block.call: 10587315.1 i/s - 1.51x slower
String#dup
vsString#+
code
Note thatString.new
is not the same as the options compared, since it isalwaysASCII-8BIT
encoded instead of the script encoding (usuallyUTF-8
).
$ ruby -v code/string/dup-vs-unary-plus.rbruby 2.4.3p205 (2017-12-14 revision 61247) [x86_64-darwin17]Calculating ------------------------------------- String#+@ 7.697M (± 1.4%) i/s - 38.634M in 5.020313s String#dup 3.566M (± 1.0%) i/s - 17.860M in 5.008377sComparison: String#+@: 7697108.3 i/s String#dup: 3566485.7 i/s - 2.16x slower
String#casecmp
vsString#casecmp?
vsString#downcase + ==
code
String#casecmp?
is available on Ruby 2.4 or later.Note thatString#casecmp
only works on characters A-Z/a-z, not all of Unicode.
$ ruby -v code/string/casecmp-vs-downcase-\=\=.rbruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-darwin19]Warming up -------------------------------------- String#casecmp? 395.796k i/100msString#downcase + == 543.958k i/100ms String#casecmp 730.028k i/100msCalculating ------------------------------------- String#casecmp? 3.687M (±10.9%) i/s - 18.602M in 5.158065sString#downcase + == 5.017M (±11.3%) i/s - 25.022M in 5.089175s String#casecmp 6.948M (± 6.0%) i/s - 35.041M in 5.062714sComparison: String#casecmp: 6948231.0 i/sString#downcase + ==: 5017089.5 i/s - 1.38x (± 0.00) slower String#casecmp?: 3686650.7 i/s - 1.88x (± 0.00) slower
String Concatenationcode
$ ruby -v code/string/concatenation.rbruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-linux]Warming up -------------------------------------- String#+ 149.298k i/100ms String#concat 151.505k i/100ms String#append 153.389k i/100ms "foo" "bar" 195.552k i/100ms "#{'foo'}#{'bar'}" 193.784k i/100msCalculating ------------------------------------- String#+ 2.977M (± 1.1%) i/s - 14.930M in 5.015179s String#concat 3.017M (± 1.3%) i/s - 15.150M in 5.023063s String#append 3.076M (± 1.2%) i/s - 15.492M in 5.037683s "foo" "bar" 5.370M (± 1.0%) i/s - 26.986M in 5.026271s "#{'foo'}#{'bar'}" 5.182M (± 4.6%) i/s - 25.967M in 5.022093sComparison: "foo" "bar": 5369594.5 i/s "#{'foo'}#{'bar'}": 5181745.7 i/s - same-ish: difference falls within error String#append: 3075719.2 i/s - 1.75x slower String#concat: 3016703.5 i/s - 1.78x slower String#+: 2977282.7 i/s - 1.80x slower
String#match
vsString.match?
vsString#start_with?
/String#end_with?
code (start)code (end)
The regular expression approaches become slower as the tested string becomeslonger. For short strings,String#match?
performs similarly toString#start_with?
/String#end_with?
.
⚠️
Sometimes you cant replace regexp withstart_with?
,
for example:"a\nb" =~ /^b/ #=> 2
but"a\nb" =~ /\Ab/ #=> nil
.⚠️
$ ruby -v code/string/start-string-checking-match-vs-start_with.rbruby 2.4.3p205 (2017-12-14 revision 61247) [x86_64-darwin17]Calculating ------------------------------------- String#=~ 1.088M (± 4.0%) i/s - 5.471M in 5.034404s String#match? 5.138M (± 5.0%) i/s - 25.669M in 5.008810s String#start_with? 6.314M (± 4.3%) i/s - 31.554M in 5.007207sComparison: String#start_with?: 6314182.0 i/s String#match?: 5138115.1 i/s - 1.23x slower String#=~: 1088461.5 i/s - 5.80x slower
$ ruby -v code/string/end-string-checking-match-vs-end_with.rb ruby 2.4.3p205 (2017-12-14 revision 61247) [x86_64-darwin17] Calculating ------------------------------------- String#=~ 918.101k (± 6.0%) i/s - 4.650M in 5.084079s String#match? 3.009M (± 6.8%) i/s - 14.991M in 5.005691s String#end_with? 4.548M (± 9.3%) i/s - 22.684M in 5.034115s Comparison: String#end_with?: 4547871.0 i/s String#match?: 3008554.5 i/s - 1.51x slower String#=~: 918100.5 i/s - 4.95x slower
String#start_with?
vsString#[].==
code
$ ruby -v code/string/end-string-checking-match-vs-end_with.rbruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-darwin14]Calculating ------------------------------------- String#start_with? 2.047M (± 4.5%) i/s - 10.242M in 5.015146s String#[0, n] == 711.802k (± 7.3%) i/s - 3.551M in 5.019543s String#[RANGE] == 651.751k (± 6.2%) i/s - 3.296M in 5.078772s String#[0...n] == 427.207k (± 5.7%) i/s - 2.136M in 5.019245sComparison: String#start_with?: 2046618.9 i/s String#[0, n] ==: 711802.3 i/s - 2.88x slower String#[RANGE] ==: 651751.2 i/s - 3.14x slower String#[0...n] ==: 427206.8 i/s - 4.79x slower
Regexp#===
vsRegexp#match
vsRegexp#match?
vsString#match
vsString#=~
vsString#match?
code
String#match?
andRegexp#match?
are available on Ruby 2.4 or later.ActiveSupportprovidesa forward compatible extension ofRegexp
for older Rubies without the speedimprovement.
⚠️
Sometimes you can't replacematch
withmatch?
,
This is only useful for cases where you are checking
for a match and not using the resultant match object.⚠️ Regexp#===
is also faster thanString#match
but you need to switch the order of arguments.
$ ruby -v code/string/===-vs-=~-vs-match.rbruby 2.4.1p111 (2017-03-22 revision 58053) [x86_64-darwin16]Calculating ------------------------------------- Regexp#match? 6.994M (± 3.0%) i/s - 35.144M in 5.029647s String#match? 6.909M (± 3.3%) i/s - 34.663M in 5.023177s String#=~ 2.784M (± 5.2%) i/s - 13.996M in 5.043168s Regexp#=== 2.702M (± 4.5%) i/s - 13.631M in 5.056215s Regexp#match 2.607M (± 4.9%) i/s - 13.025M in 5.009071s String#match 2.362M (± 5.7%) i/s - 11.817M in 5.020344sComparison: Regexp#match?: 6994107.7 i/s String#match?: 6909055.7 i/s - same-ish: difference falls within error String#=~: 2783577.8 i/s - 2.51x slower Regexp#===: 2702030.0 i/s - 2.59x slower Regexp#match: 2607484.0 i/s - 2.68x slower String#match: 2362314.8 i/s - 2.96x slower
See#59 and#62 for discussions.
String#gsub
vsString#sub
vsString#[]=
code
$ ruby -v code/string/gsub-vs-sub.rbruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-linux]Warming up -------------------------------------- String#gsub 48.360k i/100ms String#sub 45.739k i/100msString#dup["string"]= 59.896k i/100msCalculating ------------------------------------- String#gsub 647.666k (± 3.3%) i/s - 3.240M in 5.008504s String#sub 756.665k (± 2.0%) i/s - 3.796M in 5.019235sString#dup["string"]= 917.873k (± 1.8%) i/s - 4.612M in 5.026253sComparison:String#dup["string"]=: 917873.1 i/s String#sub: 756664.7 i/s - 1.21x slower String#gsub: 647665.6 i/s - 1.42x slower
String#gsub
vsString#tr
code
$ ruby -v code/string/gsub-vs-tr.rbruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-darwin14]Calculating ------------------------------------- String#gsub 38.268k i/100ms String#tr 83.210k i/100ms------------------------------------------------- String#gsub 516.604k (± 4.4%) i/s - 2.602M String#tr 1.862M (± 4.0%) i/s - 9.320MComparison: String#tr: 1861860.4 i/s String#gsub: 516604.2 i/s - 3.60x slower
String#gsub
vsString#tr
vsString#delete
code
ruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-linux]Calculating ------------------------------------- String#gsub 1.342M (± 1.3%) i/s - 6.816M in 5.079675s String#tr 2.627M (± 1.0%) i/s - 13.387M in 5.096083s String#delete 2.924M (± 0.7%) i/s - 14.889M in 5.093070s String#delete const 3.136M (± 2.6%) i/s - 15.866M in 5.064043sComparison: String#delete const: 3135559.1 i/s String#delete: 2923531.8 i/s - 1.07x slower String#tr: 2627150.5 i/s - 1.19x slower String#gsub: 1342013.4 i/s - 2.34x slower
Mutable
vsImmutable
code
$ ruby -v code/string/mutable_vs_immutable_strings.rbruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-darwin14]Calculating ------------------------------------- Without Freeze 7.279M (± 6.6%) i/s - 36.451M in 5.029785s With Freeze 9.329M (± 7.9%) i/s - 46.370M in 5.001345sComparison: With Freeze: 9329054.3 i/s Without Freeze: 7279203.1 i/s - 1.28x slower
String#sub!
vsString#gsub!
vsString#[]=
code
Note thatString#[]
will throw anIndexError
when given string or regexp not matched.
$ ruby -v code/string/sub\!-vs-gsub\!-vs-\[\]\=.rbruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-darwin14]Calculating ------------------------------------- String#['string']= 74.512k i/100ms String#sub!'string' 52.801k i/100msString#gsub!'string' 34.480k i/100ms String#[/regexp/]= 55.325k i/100ms String#sub!/regexp/ 45.770k i/100msString#gsub!/regexp/ 27.665k i/100ms------------------------------------------------- String#['string']= 1.215M (± 6.2%) i/s - 6.110M String#sub!'string' 752.731k (± 6.2%) i/s - 3.749MString#gsub!'string' 481.183k (± 4.4%) i/s - 2.414M String#[/regexp/]= 840.615k (± 5.3%) i/s - 4.205M String#sub!/regexp/ 663.075k (± 7.8%) i/s - 3.295MString#gsub!/regexp/ 342.004k (± 7.5%) i/s - 1.715MComparison: String#['string']=: 1214845.5 i/s String#[/regexp/]=: 840615.2 i/s - 1.45x slower String#sub!'string': 752731.4 i/s - 1.61x slower String#sub!/regexp/: 663075.3 i/s - 1.83x slowerString#gsub!'string': 481183.5 i/s - 2.52x slowerString#gsub!/regexp/: 342003.8 i/s - 3.55x slower
String#sub
vsString#delete_prefix
code
Ruby 2.5 introducedString#delete_prefix
.Note that this can only be used for removing characters from the start of a string.
$ ruby -v code/string/sub-vs-delete_prefix.rbruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17]Calculating -------------------------------------String#delete_prefix 4.112M (± 1.8%) i/s - 20.707M in 5.037928s String#sub 814.725k (± 1.4%) i/s - 4.088M in 5.018962sComparison:String#delete_prefix: 4111531.1 i/s String#sub: 814725.3 i/s - 5.05x slower
String#sub
vsString#chomp
vsString#delete_suffix
code
Ruby 2.5 introducedString#delete_suffix
as a counterpart todelete_prefix
. The performance gain overchomp
issmall and during some runs the difference falls within the error margin.Note that this can only be used for removing characters from the end of a string.
$ ruby -v code/string/sub-vs-chomp-vs-delete_suffix.rbruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17]Calculating ------------------------------------- String#sub 838.415k (± 1.7%) i/s - 4.214M in 5.027412s String#chomp 3.951M (± 2.1%) i/s - 19.813M in 5.017089sString#delete_suffix 4.202M (± 2.1%) i/s - 21.075M in 5.017429sComparison:String#delete_suffix: 4202201.7 i/s String#chomp: 3950921.9 i/s - 1.06x slower String#sub: 838415.3 i/s - 5.01x slower
String#unpack1
vsString#unpack[0]
code
Ruby 2.4.0 introducedunpack1
to skip creating the intermediate array object.
$ ruby -v code/string/unpack1-vs-unpack\[0\].rbruby 2.4.3p205 (2017-12-14 revision 61247) [x86_64-darwin17]Warming up -------------------------------------- String#unpack1 224.291k i/100ms String#unpack[0] 201.870k i/100msCalculating ------------------------------------- String#unpack1 4.864M (± 4.2%) i/s - 24.448M in 5.035203s String#unpack[0] 3.778M (± 4.0%) i/s - 18.976M in 5.031253sComparison: String#unpack1: 4864467.2 i/s String#unpack[0]: 3777815.6 i/s - 1.29x slower
Remove extra spaces (or other contiguous characters)code
The code is tested against contiguous spaces but should work for other chars too.
$ ruby -v code/string/remove-extra-spaces-or-other-chars.rbruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-linux]Warming up -------------------------------------- String#gsub/regex+/ 1.644k i/100ms String#squeeze 24.681k i/100msCalculating ------------------------------------- String#gsub/regex+/ 14.668k (± 5.1%) i/s - 73.980k in 5.056887s String#squeeze 372.910k (± 8.4%) i/s - 1.851M in 5.011881sComparison: String#squeeze: 372910.3 i/s String#gsub/regex+/: 14668.1 i/s - 25.42x slower
Time.iso8601
vsTime.parse
code
When expecting well-formatted data from e.g. an API,iso8601
is faster and will raise anArgumentError
on malformed input.
$ ruby -v code/time/iso8601-vs-parse.rbruby 2.4.3p205 (2017-12-14 revision 61247) [x86_64-darwin17]Warming up -------------------------------------- Time.iso8601 10.234k i/100ms Time.parse 4.228k i/100msCalculating ------------------------------------- Time.iso8601 114.485k (± 3.5%) i/s - 573.104k in 5.012008s Time.parse 43.711k (± 4.1%) i/s - 219.856k in 5.038349sComparison: Time.iso8601: 114485.1 i/s Time.parse: 43710.9 i/s - 2.62x slower
cover?
vsinclude?
code
cover?
only check if it is within the start and end,include?
needs to traverse the whole range.
$ ruby -v code/range/cover-vs-include.rbruby 2.2.3p173 (2015-08-18 revision 51636) [x86_64-linux]Calculating ------------------------------------- range#cover? 85.467k i/100ms range#include? 7.720k i/100ms range#member? 7.783k i/100ms plain compare 102.189k i/100ms------------------------------------------------- range#cover? 1.816M (± 5.6%) i/s - 9.060M range#include? 83.344k (± 5.0%) i/s - 416.880k range#member? 82.654k (± 5.0%) i/s - 412.499k plain compare 2.581M (± 6.2%) i/s - 12.876MComparison: plain compare: 2581211.8 i/s range#cover?: 1816038.5 i/s - 1.42x slower range#include?: 83343.9 i/s - 30.97x slower range#member?: 82654.1 i/s - 31.23x slower
Please!Edit this README.md thenSubmit a Awesome Pull Request!
Code example is wrong? 😢 Got better example? 😍 Excellent!
Please open an issue orOpen a Pull Request to fix it.
Thank you in advance! 😉 🍺
Share this with your #Rubyfriends! <3
Brought to you by@JuanitoFatas
Feel free to talk with me on Twitter! <3
Go faster, off the Rails - Benchmarks for your whole Rails app
Talk by Davy Stevenson @ RubyConf 2014.
Provides Big O notation benchmarking for Ruby.
Talk by Prem Sichanugrist @ Ruby Kaigi 2014.
Make your Rubies go faster with this command line tool.
This work is licensed under aCreative Commons Attribution-ShareAlike 4.0 International License.
To the extent possible under law, @JuanitoFatas has waived all copyright and related or neighboring rights to "fast-ruby".
This work belongs to the community.
About
💨 Writing Fast Ruby 😍 -- Collect Common Ruby idioms.
Topics
Resources
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Releases
Packages0
Uh oh!
There was an error while loading.Please reload this page.