Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new Style/RedundantFetchBlock cop #8147

Merged
merged 1 commit into from Jun 21, 2020

Conversation

fatkodima
Copy link
Contributor

Transferred from rubocop/rubocop-performance#126

Related reference - https://github.com/JuanitoFatas/fast-ruby#hashfetch-with-argument-vs-hashfetch--block-code

When block for fetch just returns Numeric, Symbol, Rational, Complex, true, false, nil, String or constant it is shorter (and faster) to pass an argument instead of using a block.

# bad
hash.fetch(:key) { 5 }
hash.fetch(:key) { true }
hash.fetch(:key) { false }
hash.fetch(:key) { nil }
array.fetch(5) { :value }
ENV.fetch(:key) { VALUE }

# good
hash.fetch(:key, 5)
hash.fetch(:key, true)
hash.fetch(:key, false)
hash.fetch(:key, nil)
array.fetch(5, :value)
ENV.fetch(:key, VALUE)

When hash contains a key, performance is the same for argument vs block syntaxes, but when hash is missing a key - argument version is faster.

Benchmark

# frozen_string_literal: true

require 'bundler/inline'

gemfile(true) do
  gem 'benchmark-ips'
end

hash_with_key = { key: 1 }
hash_without_key = { not_a_key: 1 }

def fast(hash)
  hash.fetch(:key, 1)
end

def slow(hash)
  hash.fetch(:key) { 1 }
end

Benchmark.ips do |x|
  x.report('fast hash_with_key') { fast(hash_with_key) }
  x.report('slow hash_with_key') { slow(hash_with_key) }
  x.compare!
end

Benchmark.ips do |x|
  x.report('fast hash_without_key') { fast(hash_without_key) }
  x.report('slow hash_without_key') { slow(hash_without_key) }
  x.compare!
end

Results

Warming up --------------------------------------
  fast hash_with_key     1.043M i/100ms
  slow hash_with_key   996.735k i/100ms
Calculating -------------------------------------
  fast hash_with_key     10.292M (± 2.9%) i/s -     52.149M in   5.072099s
  slow hash_with_key      9.940M (± 1.5%) i/s -     49.837M in   5.015103s

Comparison:
  fast hash_with_key: 10291722.7 i/s
  slow hash_with_key:  9939687.7 i/s - same-ish: difference falls within error

Warming up --------------------------------------
fast hash_without_key
                         1.017M i/100ms
slow hash_without_key
                       659.264k i/100ms
Calculating -------------------------------------
fast hash_without_key
                         10.344M (± 0.5%) i/s -     51.892M in   5.016881s
slow hash_without_key
                          6.663M (± 1.6%) i/s -     33.622M in   5.047510s

Comparison:
fast hash_without_key: 10343873.0 i/s
slow hash_without_key:  6663049.0 i/s - 1.55x  (± 0.00) slower

config/default.yml Outdated Show resolved Hide resolved
@fatkodima
Copy link
Contributor Author

Updated with suggestions.

config/default.yml Outdated Show resolved Hide resolved
@fatkodima
Copy link
Contributor Author

@koic Updated.

@bbatsov bbatsov merged commit 7b0e011 into rubocop:master Jun 21, 2020
@bbatsov
Copy link
Collaborator

bbatsov commented Jun 21, 2020

Thanks!

@Drenmi
Copy link
Collaborator

Drenmi commented Sep 16, 2020

Since this is only concerned with the performance of one case, and not the style of #fetch in general, it should probably have gone to rubocop-performance. 😅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants