rails/rails — pull request #11955 by SamSaffron (2013-08-21 05:24:58+00:00)
Title: Performance: result.fields allocates, cache the lookup

I have been trying to track down why Rails 4 AR is slower than Rails 3.

This particular one was tracked using this new functionality in Ruby head:

```

def profile_allocations(name)
GC.disable
initial_size = ObjectSpace.count_objects
ObjectSpace.trace_object_allocations do
yield
end
changes = ObjectSpace.count_objects
changes.each do |k,v|
changes[k] -= initial_size[k]
end
puts "#{name} changes"
changes.sort{|a,b| b[1] <=> a[1]}.each do |a,b|
next if b <= 0
# one extra hash for tracking
puts "#{a} #{a == :T_HASH ? b-1 : b}"
end
GC.enable
end

def profile(name, &block)
puts "Profiling all object allocation for #{name}"
GC.start
GC.disable

items = []
objs = []

ObjectSpace.trace_object_allocations do
block.call

ObjectSpace.each_object do |o|
objs << o
end

objs.each do |o|
g = ObjectSpace.allocation_generation(o)
if g
l = ObjectSpace.allocation_sourceline(o)
f = ObjectSpace.allocation_sourcefile(o)
c = ObjectSpace.allocation_class_path(o)
m = ObjectSpace.allocation_method_id(o)
items << "Allocated #{c} in #{m} #{f}:#{l}"
end
end
end

items.group_by{|x| x}.sort{|a,b| b[1].length <=> a[1].length}.each do |row, group|
puts "#{row} x #{group.length}"
end

GC.enable
profile_allocations(name, &block)
end

profile("where") do
User.where(id: 1).first
end

```

Before:

```

Allocated Array in zip /home/sam/Source/rails/activerecord/lib/active_record/result.rb:62 x 54
Allocated PG::Result in fields /home/sam/Source/rails/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb:142 x 53
Allocated PG::Result in fields /home/sam/Source/rails/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb:151 x 53
Allocated Hash in []= /home/sam/Source/rails/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb:145 x 52
Allocated ActiveRecord::AttributeMethods::TimeZoneConversion::ClassMethods in create_time_zone_conversion_attribute? /home/sam/Source/rails/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb:54 x 52
Allocated Kernel in dup /home/sam/Source/rails/activerecord/lib/active_record/result.rb:60 x 52
Allocated PG::Result in each_row /home/sam/.rbenv/versions/ruby-head/lib/ruby/gems/2.1.0/gems/pg-0.15.1/lib/pg/result.rb:10 x 33
Allocated Module in name /home/sam/Source/rails/activesupport/lib/active_support/per_thread_registry.rb:49 x 14

where changes
T_STRING 963
T_ARRAY 216
T_NODE 36
T_OBJECT 26
T_HASH 23
T_DATA 10
T_STRUCT 3

```

After

```

...
Profiling all object allocation for where
Allocated Array in zip /home/sam/Source/rails/activerecord/lib/active_record/result.rb:62 x 54
Allocated PG::Result in fields /home/sam/Source/rails/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb:142 x 53
Allocated Kernel in dup /home/sam/Source/rails/activerecord/lib/active_record/result.rb:60 x 52
Allocated ActiveRecord::AttributeMethods::TimeZoneConversion::ClassMethods in create_time_zone_conversion_attribute? /home/sam/Source/rails/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb:54 x 52
Allocated Hash in []= /home/sam/Source/rails/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb:146 x 52
Allocated PG::Result in each_row /home/sam/.rbenv/versions/ruby-head/lib/ruby/gems/2.1.0/gems/pg-0.15.1/lib/pg/result.rb:10 x 33
Allocated Module in name /home/sam/Source/rails/activesupport/lib/active_support/per_thread_registry.rb:49 x 14
Allocated Class in new /home/sam/Source/rails/activerecord/lib/active_record/model_schema.rb:232 x 10
...

where changes
T_STRING 850
T_ARRAY 215
T_NODE 36
T_OBJECT 26
T_HASH 23
T_DATA 10
T_STRUCT 3

```