1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

drop array allocations when iterating over the hash

`each_with_object` allocates an array for each kv pair.  Switching to
the slightly more verbose but less allocatey `each_pair` eliminates
array allocations.  Eliminating this allocation returns AR objects to
have constant array allocations regardless of the number of columns the
object has.

Here is test code:

```ruby
require 'active_record'

class Topic < ActiveRecord::Base
end

20.times do |i|
  Process.waitpid fork {
    ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: ':memory:'

    ActiveRecord::Base.connection.instance_eval do
      create_table(:topics) do |t|
        t.string   :title, limit: 250
        t.string   :author_name
        t.string   :author_email_address
        t.string   :parent_title
        t.string   :type
        t.string   :group
        i.times do |j|
          t.string :"aaa#{j}"
        end
        t.timestamps null: true
      end
    end

    ObjectSpace::AllocationTracer.setup(%i{type})

    Topic.create title: "aaron" # heat cache

    result = ObjectSpace::AllocationTracer.trace do
      10.times do |i|
        Topic.create title: "aaron #{i}"
      end
    end

    puts "#{Topic.columns.length},#{(result.find { |k,v| k.first == :T_ARRAY }.last.first / 10)}"
  }
end
```

Before this commit:

```
9,166
10,167
11,168
12,169
13,170
14,171
15,172
16,173
17,174
18,175
19,176
20,177
21,178
22,179
23,180
24,181
25,182
26,183
27,184
28,185
```

After:

```
9,157
10,157
11,157
12,157
13,157
14,157
15,157
16,157
17,157
18,157
19,157
20,157
21,157
22,157
23,157
24,157
25,157
26,157
27,157
28,157
```

Left side is the number of columns, right is the number of allocations
This commit is contained in:
Aaron Patterson 2015-10-15 14:44:01 -07:00
parent 3b61027092
commit 960de47f0e

View file

@ -39,7 +39,8 @@ class Hash
# hash[:a][:c] # => nil
# dup[:a][:c] # => "c"
def deep_dup
each_with_object(dup) do |(key, value), hash|
hash = dup
each_pair do |key, value|
if key.frozen? && ::String === key
hash[key] = value.deep_dup
else
@ -47,5 +48,6 @@ class Hash
hash[key.deep_dup] = value.deep_dup
end
end
hash
end
end