1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00
Is there a reason we're only taking this short cut for frozen strings and not other immutable objects commonly used as hash keys (eg. symbols and integers)?

initial tests suggest a ~30% performance improvement...

```

require 'benchmark'

class Object
  def deep_dup
    dup
  end
  alias deep_dup_all deep_dup
end

class Hash
 def deep_dup
   hash = dup
   each_pair do |key, value|
     if key.frozen? && ::String === key
       hash[key] = value.deep_dup
     else
       hash.delete(key)
       hash[key.deep_dup] = value.deep_dup
     end
   end
   hash
 end

 def deep_dup_all
   hash = dup
   each_pair do |key, value|
     if key.frozen?
       hash[key] = value.deep_dup_all
     else
       hash.delete(key)
       hash[key.deep_dup_all] = value.deep_dup_all
     end
   end
   hash
 end
end

data = Hash[('aaa'..'zzz').map {|k| [k.to_sym, k]}]

control = Benchmark.realtime do
  30.times { data.deep_dup }
end

experiment = Benchmark.realtime do
  30.times { data.deep_dup_all }
end

puts "%.3f  v  %.3f  => %d%% speed up" % [ control, experiment, (control - experiment) / control * 100 ]

```
This commit is contained in:
Daniel Pepper 2020-08-12 16:22:46 -07:00
parent 71002cfbee
commit 964e7ad156
No known key found for this signature in database
GPG key ID: B7228DC1EC451D66
2 changed files with 11 additions and 1 deletions

View file

@ -43,7 +43,7 @@ class Hash
def deep_dup
hash = dup
each_pair do |key, value|
if key.frozen? && ::String === key
if (::String === key && key.frozen?) || ::Symbol === key
hash[key] = value.deep_dup
else
hash.delete(key)

View file

@ -56,4 +56,14 @@ class DeepDupTest < ActiveSupport::TestCase
dup = hash.deep_dup
assert_equal 1, dup.keys.length
end
def test_deep_dup_with_mutable_frozen_key
key = { array: [] }.freeze
hash = { key => :value }
dup = hash.deep_dup
dup.transform_keys { |k| k[:array] << :array_element }
assert_not_equal hash.keys, dup.keys
end
end