工具介绍
:dash: 编写快速的 Ruby :heart_eyes: -- 收集常见的 Ruby 习语。
Fast Ruby [](https://github.com/fastruby/fast-ruby/actions/workflows/benchmarks.yml)
=======================================================================================================================================================================
In [Erik Michaels-Ober](https://github.com/sferik)'s great talk, 'Writing Fast Ruby': [Video @ Baruco 2014](https://www.youtube.com/watch?v=fGFM_UrSp70), [Slide](https://speakerdeck.com/sferik/writing-fast-ruby), 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` versus `tr`). Friendly reminder: Use with caution!**
Each idiom has a corresponding code example that resides in [code](code).
All results listed in README.md are running with Ruby 4.0.0 on macOS 15.6.1. Machine information: MacBook Pro (14-inch, Nov 2024), Apple M4 Pro, 48 GB RAM. Your results may vary, but you get the idea. : )
You can checkout [the GitHub Actions build](https://github.com/fastruby/fast-ruby/actions) for these benchmark results ran against different Ruby implementations.
**Let's write faster code, together! <3**
Analyze your code
-----------------
Checkout the [fasterer](https://github.com/DamirSvrtan/fasterer) project - it's a static analysis that checks speed idioms written in this repo.
Measurement Tool
-----------------
Use [benchmark-ips](https://github.com/evanphx/benchmark-ips) (2.0+).
### Template
```ruby
require "benchmark/ips"
def fast
end
def slow
end
Benchmark.ips do |x|
x.report("fast code description") { fast }
x.report("slow code description") { slow }
x.compare!
end
```
Idioms
------
### Index
- [General](#general)
- [Array](#array)
- [Date](#date)
- [Enumerable](#enumerable)
- [Hash](#hash)
- [Proc & Block](#proc--block)
- [String](#string)
- [Time](#time)
- [Range](#range)
### General
##### `attr_accessor` vs `getter and setter` [code](code/general/attr-accessor-vs-getter-and-setter.rb)
> 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.rb
ruby 4.0.0 (2025-12-25 revision 553f1675f3) +PRISM [arm64-darwin24]
Warming up --------------------------------------
getter_and_setter 1.122M i/100ms
attr_accessor 1.303M i/100ms
Calculating -------------------------------------
getter_and_setter 10.999M (± 2.1%) i/s (90.92 ns/i) - 56.083M in 5.101077s
attr_accessor 12.517M (± 1.5%) i/s (79.89 ns/i) - 63.841M in 5.101477s
Comparison:
attr_accessor: 12517008.0 i/s
getter_and_setter: 10999275.5 i/s - 1.14x slower
```
##### `begin...rescue` vs `respond_to?` for Control Flow [code](code/general/begin-rescue-vs-respond-to.rb)
```
$ ruby -v code/general/begin-rescue-vs-respond-to.rb
ruby 4.0.0 (2025-12-25 revision 553f1675f3) +PRISM [arm64-darwin24]
Warming up --------------------------------------
begin...rescue 230.601k i/100ms
respond_to? 1.915M i/100ms
Calculating -------------------------------------
begin...rescue 2.390M (± 0.9%) i/s (418.40 ns/i) - 11.991M in 5.017466s
respond_to? 18.745M (± 1.8%) i/s (53.35 ns/i) - 93.843M in 5.007990s
Comparison:
respond_to?: 18744998.8 i/s
begin...rescue: 2390076.1 i/s - 7.84x slower
```
##### `define_method` vs `module_eval` for Defining Methods [code](code/general/define_method-vs-module-eval.rb)
```
…
```
##### `String#constantize` vs a comparison for inflection [code](code/general/constantize-vs-comparison.rb)
ActiveSupport's [String#constantize](https://guides.rubyonrails.org/active_support_core_extensions.html#constantize) "resolves the constant reference expression in its receiver".
[Read the rationale here](https://github.com/fastruby/fast-ruby/pull/200)
```
…
```
##### `raise` vs `E2MM#Raise` for raising (and defining) exceptions [code](code/general/raise-vs-e2mmap.rb)
Ruby's [Exception2MessageMapper module](http://ruby-doc.org/stdlib-2.2.0/libdoc/e2mmap/rdoc/index.html) allows one to define and raise exceptions with predefined messages.
```
…
```
##### `loop` vs `while true` [code](code/general/loop-vs-while-true.rb)
```
$ ruby -v code/general/loop-vs-while-true.rb
ruby 4.0.0 (2025-12-25 revision 553f1675f3) +PRISM [arm64-darwin24]
Warming up --------------------------------------
While Loop 1.000 i/100ms
Kernel loop 1.000 i/100ms
Calculating -------------------------------------
While Loop 1.331 (± 0.0%) i/s (751.41 ms/i) - 7.000 in 5.260296s
Kernel loop 0.528 (± 0.0%) i/s (1.89 s/i) - 3.000 in 5.709880s
Comparison:
While Loop: 1.3 i/s
Kernel loop: 0.5 i/s - 2.52x slower
```
##### `ancestors.include?` vs `<=` [code](code/general/inheritance-check.rb)
```
$ ruby -v code/general/inheritance-check.rb
ruby 4.0.0 (2025-12-25 revision 553f1675f3) +PRISM [arm64-darwin24]
Warming up --------------------------------------
less than or equal 1.150M i/100ms
ancestors.include? 179.611k i/100ms
Calculating -------------------------------------
less than or equal 11.426M (± 1.4%) i/s (87.52 ns/i) - 57.493M in 5.032894s
ancestors.include? 1.880M (± 1.0%) i/s (531.84 ns/i) - 9.519M in 5.063302s
Comparison:
less than or equal: 11425608.6 i/s
ancestors.include?: 1880262.8 i/s - 6.08x slower
```
### Method Invocation
##### `call` vs `send` vs `method_missing` [code](code/method/call-vs-send-vs-method_missing.rb)
```
…
```
##### Normal way to apply method vs `&method(...)` [code](code/general/block-apply-method.rb)
```
$ ruby -v code/general/block-apply-method.rb
ruby 4.0.0 (2025-12-25 revision 553f1675f3) +PRISM [arm64-darwin24]
Warming up --------------------------------------
normal 698.536k i/100ms
&method 322.901k i/100ms
Calculating -------------------------------------
normal 6.978M (± 0.9%) i/s (143.31 ns/i) - 34.927M in 5.005888s
&method 3.214M (± 0.5%) i/s (311.12 ns/i) - 16.145M in 5.023139s
Comparison:
normal: 6977718.6 i/s
&method: 3214214.6 i/s - 2.17x slower
```
##### Function with single Array argument vs splat arguments [code](code/general/array-argument-vs-splat-arguments.rb)
```
…
```
##### Hash vs OpenStruct on access assuming you already have a Hash or an OpenStruct [code](code/general/hash-vs-openstruct-on-access.rb)
```
$ ruby -v code/general/hash-vs-openstruct-on-access.rb
ruby 4.0.0 (2025-12-25 revision 553f1675f3) +PRISM [arm64-darwin24]
Warming up --------------------------------------
Hash 2.028M i/100ms
OpenStruct 1.478M i/100ms
Calculating -------------------------------------
Hash 20.290M (± 0.5%) i/s (49.29 ns/i) - 103.430M in 5.097815s
OpenStruct 14.807M (± 0.6%) i/s (67.54 ns/i) - 75.387M in 5.091599s
Comparison:
Hash: 20289714.4 i/s
OpenStruct: 14806564.8 i/s - 1.37x slower
```
##### Hash vs OpenStruct (creation) [code](code/general/hash-vs-openstruct.rb)
```
ruby 4.0.0 (2025-12-25 revision 553f1675f3) +PRISM [arm64-darwin24]
Warming up --------------------------------------
Hash 2.652M i/100ms
OpenStruct 31.241k i/100ms
Calculating -------------------------------------
Hash 26.301M (± 1.0%) i/s (38.02 ns/i) - 132.614M in 5.042769s
OpenStruct 313.539k (± 1.8%) i/s (3.19 μs/i) - 1.593M in 5.083245s
Comparison:
Hash: 26300561.3 i/s
OpenStruct: 313538.7 i/s - 83.88x slower
```
##### Kernel#format vs Float#round().to_s [code](code/general/format-vs-round-and-to-s.rb)
```
…
```
### Array
##### `Array#bsearch` vs `Array#find` [code](code/array/bsearch-vs-find.rb)
**WARNING:** `bsearch` ONLY works on *sorted array*. More details please see [#29](https://github.com/fastruby/fast-ruby/issues/29).
```
$ ruby -v code/array/bsearch-vs-find.rb
ruby 4.0.0 (2025-12-25 revision 553f1675f3) +PRISM [arm64-darwin24]
Warming up --------------------------------------
find 1.000 i/100ms
bsearch 189.387k i/100ms
Calculating -------------------------------------
find 0.703 (± 0.0%) i/s (1.42 s/i) - 4.000 in 5.692110s
bsearch 1.893M (± 1.6%) i/s (528.21 ns/i) - 9.469M in 5.003152s
Comparison:
bsearch: 1893194.5 i/s
find: 0.7 i/s - 2691605.95x slower
```
##### `Array#length` vs `Array#size` vs `Array#count` [code](code/array/length-vs-size-vs-count.rb)
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`](https://github.com/ruby/ruby/blob/f8fb526ad9e9f31453bffbc908b6a986736e21a7/array.c#L5817-L5818).
```
…
```
##### `Array#shuffle.first` vs `Array#sample` [code](code/array/shuffle-first-vs-sample.rb)
> `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.
> —— @sferik [rails/rails#17245](https://github.com/rails/rails/pull/17245)
```
$ ruby -v code/array/shuffle-first-vs-sample.rb
ruby 4.0.0 (2025-12-25 revision 553f1675f3) +PRISM [arm64-darwin24]
Warming up --------------------------------------
Array#shuffle.first 88.991k i/100ms
Array#sample 2.503M i/100ms
Calculating -------------------------------------
Array#shuffle.first 891.780k (± 0.5%) i/s (1.12 μs/i) - 4.539M in 5.089459s
Array#sample 25.133M (± 0.6%) i/s (39.79 ns/i) - 127.660M in 5.079516s
Comparison:
Array#sample: 25133099.8 i/s
Array#shuffle.first: 891779.8 i/s - 28.18x slower
```
##### `Array#[](0)` vs `Array#first` [code](code/array/array-first-vs-index.rb)
```
$ ruby -v code/array/array-first-vs-index.rb
ruby 4.0.0 (2025-12-25 revision 553f1675f3) +PRISM [arm64-darwin24]
Warming up --------------------------------------
Array#[0] 3.922M i/100ms
Array#first 3.697M i/100ms
Calculating -------------------------------------
Array#[0] 39.347M (± 0.7%) i/s (25.41 ns/i) - 200.006M in 5.083307s
Array#first 37.289M (± 1.1%) i/s (26.82 ns/i) - 188.546M in 5.056917s
Comparison:
Array#[0]: 39347487.6 i/s
Array#first: 37288935.3 i/s - 1.06x slower
```
##### `Array#[](-1)` vs `Array#last` [code](code/array/array-last-vs-index.rb)
```
$ ruby -v code/array/array-last-vs-index.rb
ruby 4.0.0 (2025-12-25 revision 553f1675f3) +PRISM [arm64-darwin24]
Warming up --------------------------------------
Array#[-1] 3.906M i/100ms
Array#last 3.727M i/100ms
Calculating -------------------------------------
Array#[-1] 39.303M (± 0.7%) i/s (25.44 ns/i) - 199.228M in 5.069210s
Array#last 37.280M (± 1.1%) i/s (26.82 ns/i) - 190.075M in 5.099254s
Comparison:
Array#[-1]: 39303493.5 i/s
Array#last: 37279563.5 i/s - 1.05x slower
```
##### `Array#insert` vs `Array#unshift` [code](code/array/insert-vs-unshift.rb)
```
$ ruby -v code/array/insert-vs-unshift.rb
ruby 4.0.0 (2025-12-25 revision 553f1675f3) +PRISM [arm64-darwin24]
Warming up --------------------------------------
Array#unshift 37.000 i/100ms
Array#insert 1.000 i/100ms
Calculating -------------------------------------
Array#unshift 379.798 (± 1.1%) i/s (2.63