Skip to content

Add Hash#values_at vs Array#map { Hash#[] } comparison - #174

Open
kewlar wants to merge 6 commits into
fastruby:mainfrom
kewlar:hash-values_at
Open

Add Hash#values_at vs Array#map { Hash#[] } comparison#174
kewlar wants to merge 6 commits into
fastruby:mainfrom
kewlar:hash-values_at

Conversation

@kewlar

Copy link
Copy Markdown
Contributor

No description provided.

@kewlar

Copy link
Copy Markdown
ContributorAuthor

DO NOT MERGE YET.

Just noticed that #values_at and #slice#values return different results when Hash members are missing 🤦‍♂️

Will update the code and the benchmark, and see if it's still worth merging.

@Arcovion

Copy link
Copy Markdown
Collaborator

This is faster than slice on my machine and returns the same as values_at:

KEYS.map{ |key| HASH[key]}

@kewlar

Copy link
Copy Markdown
ContributorAuthor

@Arcovion, cool! Will add it to the benchmark, too. Thanks!

@kewlar

kewlar commented Mar 21, 2019

Copy link
Copy Markdown
ContributorAuthor

Added more ways of slicing Hash values, and organized them into three separate cases:

  • when all keys are expected to exist in the Hash (Hash#values_at);
  • when some keys may not exist in the Hash, and we care about the non-existing keys (Hash#values_at);
  • when some keys may not exist in the Hash, and we care only about the existing keys (Hash#slice#values).

Thoughts?

@etagwerker

Copy link
Copy Markdown
Member

@kewlar I see the value in adding code/hash/values_at-vs-map.rb because it is quite straightforward and natural. But I don't see the value in adding the other benchmarks. The other examples are doing too many things and it is hard to tell which part makes one slower than the other.

I'm happy to add code/hash/values_at-vs-map.rb if you submit just that in this PR or another PR.

@kewlarkewlar changed the title Add Hash#values_at vs Hash#slice#values comparisonAdd Hash#values_at vs Array#map { Hash#[] } comparisonMar 2, 2023
@kewlar

Copy link
Copy Markdown
ContributorAuthor

@etagwerker, thanks for the feedback!

You're right, the other benchmarks are really just edge cases, and don't contribute much. And Hash#values_at is the best performing in them anyway.

I've removed the redundant benchmarks. How does it look now?

d: 'qux'
}.freeze

# Some of the keys may not exist in the hash; we want to keep the default values.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For existing keys, the benchmark does not change much.

# frozen_string_literal: truerequire'benchmark/ips'HASH={a: 'foo',b: 'bar',c: 'baz',d: 'qux'}.freeze# the keys exist in the hashKEYS=%i[abcd].freezedeffastestHASH.values_at(*KEYS)enddefslowHASH.fetch_values(*KEYS)enddefslowestKEYS.map{ |key| HASH[key]}endBenchmark.ipsdo |x|
x.report('Hash#values_at '){fastest}x.report('Hash#fetch_values '){slow}x.report('Array#map { Hash#[] }'){slowest}x.compare!end
ydakuka@yauhenid:~/ruby-docker-app$ docker run ruby-app Warming up --------------------------------------Hash#values_at  405.669k i/100msHash#fetch_values  354.263k i/100msArray#map { Hash#[] } 221.960k i/100msCalculating -------------------------------------Hash#values_at  4.191M (± 4.7%) i/s - 21.095M in 5.045130sHash#fetch_values  3.895M (± 6.8%) i/s - 19.484M in 5.031663sArray#map { Hash#[] } 2.429M (± 6.1%) i/s - 12.208M in 5.048470sComparison:Hash#values_at : 4190993.6 i/sHash#fetch_values : 3894866.5 i/s - same-ish: difference falls within errorArray#map { Hash#[] }: 2429428.6 i/s - 1.73x slower

Sign up for freeto 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.

4 participants

@kewlar@Arcovion@etagwerker@ydakuka