-
Notifications
You must be signed in to change notification settings - Fork 21.4k
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
Use Hash#each_key instead of Hash#keys.each #17099
Conversation
@@ -14,7 +14,7 @@ class Hash | |||
# search(options.slice(*valid_keys)) | |||
def slice(*keys) | |||
keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true) | |||
keys.each_with_object(self.class.new) { |k, hash| hash[k] = self[k] if has_key?(k) } | |||
each_key.with_object(self.class.new) { |k, hash| hash[k] = self[k] if has_key?(k) } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you measured this case? I believe this will create fibers that may make it slower than the previous implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not yet. I will benchmark it now and remove this part of the commit if it is not performant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rafaelfranca Here’s the benchmark for this case. Looks to be about the same to me. Maybe slightly faster than before, but within the margin of error. Let me know if you prefer it the way it was before.
require 'benchmark/ips'
HASH = Hash[*('aa'..'zz')]
def slow
HASH.keys.each_with_object({}) { |k, o| o[k] = true }
end
def fast
HASH.each_key.with_object({}) { |k, o| o[k] = true }
end
Benchmark.ips do |x|
x.report('slow') { fast }
x.report('fast') { slow }
end
Ruby 2.1.2p95
slow 6299.5 (±33.7%) i/s - 26784 in 5.030937s
fast 7205.2 (±35.8%) i/s - 30912 in 5.105253s
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good this way
👍 |
For some reason build is red. Could you take a look? |
@rafaelfranca There were a few cases where we were calling |
Hash#keys.each allocates an array of keys; Hash#each_key iterates through the keys without allocating a new array. This is the reason why Hash#each_key exists.
Use Hash#each_key instead of Hash#keys.each
Thanks @sferik ! ❤️ |
For some reason I get opposite results from @sferik on ruby 2.1.2p95 for this particular benchmark,
For all other of these micro optimizations I tried, my benchmarks results match @sferik , but not this one. Not sure why. Has anyone ran this benchmark? |
@pkmiec I ran the same benchmark using ruby 2.1.3p242 and also get the opposite result:
I should also include similar results for 2.1.2-p95:
|
It’s possible I made a mistake. 😳 On CRuby 1.9.3p545 and 2.0.0p481 and JRuby 1.7.16, the |
@sferik I'm curious to find out the reason for this. Please let us know if you have a chance to figure it out. |
If you compare the benchmark between MRI 2.1 and 2.0, the performance of As far as I can tell, this improvement is a result of this change to use the In theory, a similar optimization could be made in the Please keep in mind that I’m not a expert with C code, in general, or CRuby, in particular, so I may be reading this wrong. |
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.This is a more comprehensive pull request that closes #17083.
Benchmark