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

fatkodima commented Jun 12, 2020

Transferred from rubocop-hq/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 fatkodima force-pushed the fatkodima:redundant-fetch-block branch from fdf5be4 to 4648cdc Jun 14, 2020
@fatkodima
Copy link
Contributor Author

fatkodima commented Jun 14, 2020

Updated with suggestions.

config/default.yml Outdated Show resolved Hide resolved
@fatkodima fatkodima force-pushed the fatkodima:redundant-fetch-block branch from 4648cdc to 72b50b5 Jun 14, 2020
@fatkodima
Copy link
Contributor Author

fatkodima commented Jun 14, 2020

@koic Updated.

@bbatsov bbatsov merged commit 7b0e011 into rubocop-hq:master Jun 21, 2020
26 checks passed
26 checks passed
windows 2.4
Details
windows 2.5
Details
windows 2.6
Details
windows 2.7
Details
windows mingw
Details
ci/circleci: cc-setup Your tests passed on CircleCI!
Details
ci/circleci: cc-upload-coverage Your tests passed on CircleCI!
Details
ci/circleci: documentation-checks Your tests passed on CircleCI!
Details
ci/circleci: jruby-9.2-ascii_spec Your tests passed on CircleCI!
Details
ci/circleci: jruby-9.2-rubocop Your tests passed on CircleCI!
Details
ci/circleci: jruby-9.2-spec Your tests passed on CircleCI!
Details
ci/circleci: ruby-2.4-ascii_spec Your tests passed on CircleCI!
Details
ci/circleci: ruby-2.4-rubocop Your tests passed on CircleCI!
Details
ci/circleci: ruby-2.4-spec Your tests passed on CircleCI!
Details
ci/circleci: ruby-2.5-ascii_spec Your tests passed on CircleCI!
Details
ci/circleci: ruby-2.5-rubocop Your tests passed on CircleCI!
Details
ci/circleci: ruby-2.5-spec Your tests passed on CircleCI!
Details
ci/circleci: ruby-2.6-ascii_spec Your tests passed on CircleCI!
Details
ci/circleci: ruby-2.6-rubocop Your tests passed on CircleCI!
Details
ci/circleci: ruby-2.6-spec Your tests passed on CircleCI!
Details
ci/circleci: ruby-2.7-ascii_spec Your tests passed on CircleCI!
Details
ci/circleci: ruby-2.7-rubocop Your tests passed on CircleCI!
Details
ci/circleci: ruby-2.7-spec Your tests passed on CircleCI!
Details
ci/circleci: ruby-head-ascii_spec Your tests passed on CircleCI!
Details
ci/circleci: ruby-head-rubocop Your tests passed on CircleCI!
Details
ci/circleci: ruby-head-spec Your tests passed on CircleCI!
Details
@bbatsov
Copy link
Collaborator

bbatsov commented Jun 21, 2020

Thanks!

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

Successfully merging this pull request may close these issues.

None yet

3 participants
You can’t perform that action at this time.